diff options
Diffstat (limited to 'libphobos')
70 files changed, 8329 insertions, 9309 deletions
diff --git a/libphobos/Makefile.in b/libphobos/Makefile.in index a8f7e160742..2e9360a5238 100644 --- a/libphobos/Makefile.in +++ b/libphobos/Makefile.in @@ -15,7 +15,7 @@ @SET_MAKE@ # Makefile for the toplevel directory of the D Standard library. -# Copyright (C) 2006-2021 Free Software Foundation, Inc. +# Copyright (C) 2006-2022 Free Software Foundation, Inc. # # GCC is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 3aa798a543c..251d78de19b 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -759e60231a12482a1e1df5f891964e270dae0a1b +dbd0c874a345438b8b4379a67525a933436d039a The first line of this file holds the git revision number of the last merge done from the dlang/druntime repository. diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index 2d0bd37babf..ba641315664 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -170,7 +170,7 @@ DRUNTIME_CSOURCES = core/stdc/errno_.c DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/builtins.d core/checkedint.d core/cpuid.d core/demangle.d \ core/exception.d core/gc/config.d core/gc/gcinterface.d \ - core/gc/registry.d core/internal/abort.d \ + core/gc/registry.d core/int128.d core/internal/abort.d \ core/internal/array/appending.d core/internal/array/capacity.d \ core/internal/array/casting.d core/internal/array/comparison.d \ core/internal/array/concatenation.d core/internal/array/construction.d \ @@ -425,4 +425,4 @@ DRUNTIME_DSOURCES_WINDOWS = core/sys/windows/accctrl.d \ core/sys/windows/winuser.d core/sys/windows/winver.d \ core/sys/windows/wtsapi32.d core/sys/windows/wtypes.d -DRUNTIME_DISOURCES = __main.di +DRUNTIME_DISOURCES = __builtins.di __main.di diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index bb936ddc1ff..1c64d35b164 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -15,7 +15,7 @@ @SET_MAKE@ # Makefile for the D runtime library. -# Copyright (C) 2012-2021 Free Software Foundation, Inc. +# Copyright (C) 2012-2022 Free Software Foundation, Inc. # # GCC is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -190,7 +190,7 @@ am__dirstamp = $(am__leading_dot)dirstamp am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ core/builtins.lo core/checkedint.lo core/cpuid.lo \ core/demangle.lo core/exception.lo core/gc/config.lo \ - core/gc/gcinterface.lo core/gc/registry.lo \ + core/gc/gcinterface.lo core/gc/registry.lo core/int128.lo \ core/internal/abort.lo core/internal/array/appending.lo \ core/internal/array/capacity.lo core/internal/array/casting.lo \ core/internal/array/comparison.lo \ @@ -834,7 +834,7 @@ DRUNTIME_CSOURCES = core/stdc/errno_.c DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/builtins.d core/checkedint.d core/cpuid.d core/demangle.d \ core/exception.d core/gc/config.d core/gc/gcinterface.d \ - core/gc/registry.d core/internal/abort.d \ + core/gc/registry.d core/int128.d core/internal/abort.d \ core/internal/array/appending.d core/internal/array/capacity.d \ core/internal/array/casting.d core/internal/array/comparison.d \ core/internal/array/concatenation.d core/internal/array/construction.d \ @@ -1089,7 +1089,7 @@ DRUNTIME_DSOURCES_WINDOWS = core/sys/windows/accctrl.d \ core/sys/windows/winuser.d core/sys/windows/winver.d \ core/sys/windows/wtsapi32.d core/sys/windows/wtypes.d -DRUNTIME_DISOURCES = __main.di +DRUNTIME_DISOURCES = __builtins.di __main.di all: all-am .SUFFIXES: @@ -1187,6 +1187,7 @@ core/gc/$(am__dirstamp): core/gc/config.lo: core/gc/$(am__dirstamp) core/gc/gcinterface.lo: core/gc/$(am__dirstamp) core/gc/registry.lo: core/gc/$(am__dirstamp) +core/int128.lo: core/$(am__dirstamp) core/internal/$(am__dirstamp): @$(MKDIR_P) core/internal @: > core/internal/$(am__dirstamp) diff --git a/libphobos/libdruntime/__builtins.di b/libphobos/libdruntime/__builtins.di new file mode 100644 index 00000000000..cd64881529e --- /dev/null +++ b/libphobos/libdruntime/__builtins.di @@ -0,0 +1,40 @@ +/* This D file is implicitly imported by all ImportC source files. + * It provides definitions for C compiler builtin functions and declarations. + * The purpose is to make it unnecessary to hardwire them into the compiler. + * As the leading double underscore suggests, this is for internal use only. + * + * Copyright: Copyright Digital Mars 2022 + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Walter Bright + * Source: $(DRUNTIMESRC __builtins.d) + */ + + +module __builtins; + +/* gcc relies on internal __builtin_xxxx functions and templates to + * accomplish <stdarg.h>. D does the same thing with templates in core.stdc.stdarg. + * Here, we redirect the gcc builtin declarations to the equivalent + * ones in core.stdc.stdarg, thereby avoiding having to hardware them + * into the D compiler. + */ + +import core.stdc.stdarg; + +alias va_list = core.stdc.stdarg.va_list; + +version (Posix) +{ + version (X86_64) + alias __va_list_tag = core.stdc.stdarg.__va_list_tag; +} + +alias __builtin_va_start = core.stdc.stdarg.va_start; + +alias __builtin_va_end = core.stdc.stdarg.va_end; + +alias __builtin_va_copy = core.stdc.stdarg.va_copy; + +/* dmd's ImportC rewrites __builtin_va_arg into an instantiation of va_arg + */ +alias va_arg = core.stdc.stdarg.va_arg; diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d index 1915fb0844a..930e0cd9c24 100644 --- a/libphobos/libdruntime/core/demangle.d +++ b/libphobos/libdruntime/core/demangle.d @@ -2471,7 +2471,7 @@ private template hasPlainMangling(FT) if (is(FT == function)) { enum lnk = __traits(getLinkage, FT); // C || Windows - enum hasPlainMangling = lnk == "C" || lnk == "Windows"; + enum hasPlainMangling = lnk == "C" || lnk == "Windows" || lnk == "System"; } @safe pure nothrow unittest diff --git a/libphobos/libdruntime/core/int128.d b/libphobos/libdruntime/core/int128.d new file mode 100644 index 00000000000..aad2cf23942 --- /dev/null +++ b/libphobos/libdruntime/core/int128.d @@ -0,0 +1,919 @@ +/* 128 bit integer arithmetic. + * + * Not optimized for speed. + * + * Copyright: Copyright D Language Foundation 2022. + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Authors: Walter Bright + * Source: $(DRUNTIMESRC core/_int128.d) + */ + +module core.int128; + +nothrow: +@safe: +@nogc: + +alias I = long; +alias U = ulong; +enum Ubits = uint(U.sizeof * 8); + +align(16) struct Cent +{ + U lo; // low 64 bits + U hi; // high 64 bits +} + +enum One = Cent(1); +enum Zero = Cent(); +enum MinusOne = neg(One); + +/***************************** + * Test against 0 + * Params: + * c = Cent to test + * Returns: + * true if != 0 + */ +pure +bool tst(Cent c) +{ + return c.hi || c.lo; +} + + +/***************************** + * Complement + * Params: + * c = Cent to complement + * Returns: + * complemented value + */ +pure +Cent com(Cent c) +{ + c.lo = ~c.lo; + c.hi = ~c.hi; + return c; +} + +/***************************** + * Negate + * Params: + * c = Cent to negate + * Returns: + * negated value + */ +pure +Cent neg(Cent c) +{ + if (c.lo == 0) + c.hi = -c.hi; + else + { + c.lo = -c.lo; + c.hi = ~c.hi; + } + return c; +} + +/***************************** + * Increment + * Params: + * c = Cent to increment + * Returns: + * incremented value + */ +pure +Cent inc(Cent c) +{ + return add(c, One); +} + +/***************************** + * Decrement + * Params: + * c = Cent to decrement + * Returns: + * incremented value + */ +pure +Cent dec(Cent c) +{ + return sub(c, One); +} + +/***************************** + * Shift left one bit + * Params: + * c = Cent to shift + * Returns: + * shifted value + */ +pure +Cent shl1(Cent c) +{ + c.hi = (c.hi << 1) | (cast(I)c.lo < 0); + c.lo <<= 1; + return c; +} + +/***************************** + * Unsigned shift right one bit + * Params: + * c = Cent to shift + * Returns: + * shifted value + */ +pure +Cent shr1(Cent c) +{ + c.lo = (c.lo >> 1) | ((c.hi & 1) << (Ubits - 1)); + c.hi >>= 1; + return c; +} + + +/***************************** + * Arithmetic shift right one bit + * Params: + * c = Cent to shift + * Returns: + * shifted value + */ +pure +Cent sar1(Cent c) +{ + c.lo = (c.lo >> 1) | ((c.hi & 1) << (Ubits - 1)); + c.hi = cast(I)c.hi >> 1; + return c; +} + +/***************************** + * Shift left n bits + * Params: + * c = Cent to shift + * n = number of bits to shift + * Returns: + * shifted value + */ +pure +Cent shl(Cent c, uint n) +{ + if (n >= Ubits * 2) + return Zero; + + if (n >= Ubits) + { + c.hi = c.lo << (n - Ubits); + c.lo = 0; + } + else + { + c.hi = ((c.hi << n) | (c.lo >> (Ubits - n - 1) >> 1)); + c.lo = c.lo << n; + } + return c; +} + +/***************************** + * Unsigned shift right n bits + * Params: + * c = Cent to shift + * n = number of bits to shift + * Returns: + * shifted value + */ +pure +Cent shr(Cent c, uint n) +{ + if (n >= Ubits * 2) + return Zero; + + if (n >= Ubits) + { + c.lo = c.hi >> (n - Ubits); + c.hi = 0; + } + else + { + c.lo = ((c.lo >> n) | (c.hi << (Ubits - n - 1) << 1)); + c.hi = c.hi >> n; + } + return c; +} + +/***************************** + * Arithmetic shift right n bits + * Params: + * c = Cent to shift + * n = number of bits to shift + * Returns: + * shifted value + */ +pure +Cent sar(Cent c, uint n) +{ + const signmask = -(c.hi >> (Ubits - 1)); + const signshift = (Ubits * 2) - n; + c = shr(c, n); + + // Sign extend all bits beyond the precision of Cent. + if (n >= Ubits * 2) + { + c.hi = signmask; + c.lo = signmask; + } + else if (signshift >= Ubits * 2) + { + } + else if (signshift >= Ubits) + { + c.hi &= ~(U.max << (signshift - Ubits)); + c.hi |= signmask << (signshift - Ubits); + } + else + { + c.hi = signmask; + c.lo &= ~(U.max << signshift); + c.lo |= signmask << signshift; + } + return c; +} + +/***************************** + * Rotate left one bit + * Params: + * c = Cent to rotate + * Returns: + * rotated value + */ +pure +Cent rol1(Cent c) +{ + int carry = cast(I)c.hi < 0; + + c.hi = (c.hi << 1) | (cast(I)c.lo < 0); + c.lo = (c.lo << 1) | carry; + return c; +} + +/***************************** + * Rotate right one bit + * Params: + * c = Cent to rotate + * Returns: + * rotated value + */ +pure +Cent ror1(Cent c) +{ + int carry = c.lo & 1; + c.lo = (c.lo >> 1) | (cast(U)(c.hi & 1) << (Ubits - 1)); + c.hi = (c.hi >> 1) | (cast(U)carry << (Ubits - 1)); + return c; +} + + +/***************************** + * Rotate left n bits + * Params: + * c = Cent to rotate + * n = number of bits to rotate + * Returns: + * rotated value + */ +pure +Cent rol(Cent c, uint n) +{ + n &= Ubits * 2 - 1; + Cent l = shl(c, n); + Cent r = shr(c, Ubits * 2 - n); + return or(l, r); +} + +/***************************** + * Rotate right n bits + * Params: + * c = Cent to rotate + * n = number of bits to rotate + * Returns: + * rotated value + */ +pure +Cent ror(Cent c, uint n) +{ + n &= Ubits * 2 - 1; + Cent r = shr(c, n); + Cent l = shl(c, Ubits * 2 - n); + return or(r, l); +} + +/**************************** + * And c1 & c2. + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * c1 & c2 + */ +pure +Cent and(Cent c1, Cent c2) +{ + return Cent(c1.lo & c2.lo, c1.hi & c2.hi); +} + +/**************************** + * Or c1 | c2. + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * c1 | c2 + */ +pure +Cent or(Cent c1, Cent c2) +{ + return Cent(c1.lo | c2.lo, c1.hi | c2.hi); +} + +/**************************** + * Xor c1 ^ c2. + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * c1 ^ c2 + */ +pure +Cent xor(Cent c1, Cent c2) +{ + return Cent(c1.lo ^ c2.lo, c1.hi ^ c2.hi); +} + +/**************************** + * Add c1 to c2. + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * c1 + c2 + */ +pure +Cent add(Cent c1, Cent c2) +{ + U r = cast(U)(c1.lo + c2.lo); + return Cent(r, cast(U)(c1.hi + c2.hi + (r < c1.lo))); +} + +/**************************** + * Subtract c2 from c1. + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * c1 - c2 + */ +pure +Cent sub(Cent c1, Cent c2) +{ + return add(c1, neg(c2)); +} + +/**************************** + * Multiply c1 * c2. + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * c1 * c2 + */ +pure +Cent mul(Cent c1, Cent c2) +{ + enum mulmask = (1UL << (Ubits / 2)) - 1; + enum mulshift = Ubits / 2; + + // This algorithm splits the operands into 4 words, then computes and sums + // the partial products of each part. + const c2l0 = c2.lo & mulmask; + const c2l1 = c2.lo >> mulshift; + const c2h0 = c2.hi & mulmask; + const c2h1 = c2.hi >> mulshift; + + const c1l0 = c1.lo & mulmask; + U r0 = c1l0 * c2l0; + U r1 = c1l0 * c2l1 + (r0 >> mulshift); + U r2 = c1l0 * c2h0 + (r1 >> mulshift); + U r3 = c1l0 * c2h1 + (r2 >> mulshift); + + const c1l1 = c1.lo >> mulshift; + r1 = c1l1 * c2l0 + (r1 & mulmask); + r2 = c1l1 * c2l1 + (r2 & mulmask) + (r1 >> mulshift); + r3 = c1l1 * c2h0 + (r3 & mulmask) + (r2 >> mulshift); + + const c1h0 = c1.hi & mulmask; + r2 = c1h0 * c2l0 + (r2 & mulmask); + r3 = c1h0 * c2l1 + (r3 & mulmask) + (r2 >> mulshift); + + const c1h1 = c1.hi >> mulshift; + r3 = c1h1 * c2l0 + (r3 & mulmask); + + return Cent((r0 & mulmask) + (r1 & mulmask) * (mulmask + 1), + (r2 & mulmask) + (r3 & mulmask) * (mulmask + 1)); + +} + + +/**************************** + * Unsigned divide c1 / c2. + * Params: + * c1 = dividend + * c2 = divisor + * Returns: + * quotient c1 / c2 + */ +pure +Cent udiv(Cent c1, Cent c2) +{ + Cent modulus; + return udivmod(c1, c2, modulus); +} + +/**************************** + * Unsigned divide c1 / c2. The remainder after division is stored to modulus. + * Params: + * c1 = dividend + * c2 = divisor + * modulus = set to c1 % c2 + * Returns: + * quotient c1 / c2 + */ +pure +Cent udivmod(Cent c1, Cent c2, out Cent modulus) +{ + //printf("udiv c1(%llx,%llx) c2(%llx,%llx)\n", c1.lo, c1.hi, c2.lo, c2.hi); + // Based on "Unsigned Doubleword Division" in Hacker's Delight + import core.bitop; + + // Divides a 128-bit dividend by a 64-bit divisor. + // The result must fit in 64 bits. + static U udivmod128_64(Cent c1, U c2, out U modulus) + { + // We work in base 2^^32 + enum base = 1UL << 32; + enum divmask = (1UL << (Ubits / 2)) - 1; + enum divshift = Ubits / 2; + + // Check for overflow and divide by 0 + if (c1.hi >= c2) + { + modulus = 0UL; + return ~0UL; + } + + // Computes [num1 num0] / den + static uint udiv96_64(U num1, uint num0, U den) + { + // Extract both digits of the denominator + const den1 = cast(uint)(den >> divshift); + const den0 = cast(uint)(den & divmask); + // Estimate ret as num1 / den1, and then correct it + U ret = num1 / den1; + const t2 = (num1 % den1) * base + num0; + const t1 = ret * den0; + if (t1 > t2) + ret -= (t1 - t2 > den) ? 2 : 1; + return cast(uint)ret; + } + + // Determine the normalization factor. We multiply c2 by this, so that its leading + // digit is at least half base. In binary this means just shifting left by the number + // of leading zeros, so that there's a 1 in the MSB. + // We also shift number by the same amount. This cannot overflow because c1.hi < c2. + const shift = (Ubits - 1) - bsr(c2); + c2 <<= shift; + U num2 = c1.hi; + num2 <<= shift; + num2 |= (c1.lo >> (-shift & 63)) & (-cast(I)shift >> 63); + c1.lo <<= shift; + + // Extract the low digits of the numerator (after normalizing) + const num1 = cast(uint)(c1.lo >> divshift); + const num0 = cast(uint)(c1.lo & divmask); + + // Compute q1 = [num2 num1] / c2 + const q1 = udiv96_64(num2, num1, c2); + // Compute the true (partial) remainder + const rem = num2 * base + num1 - q1 * c2; + // Compute q0 = [rem num0] / c2 + const q0 = udiv96_64(rem, num0, c2); + + modulus = (rem * base + num0 - q0 * c2) >> shift; + return (cast(U)q1 << divshift) | q0; + } + + // Special cases + if (!tst(c2)) + { + // Divide by zero + modulus = Zero; + return com(modulus); + } + if (c1.hi == 0 && c2.hi == 0) + { + // Single precision divide + modulus = Cent(c1.lo % c2.lo); + return Cent(c1.lo / c2.lo); + } + if (c1.hi == 0) + { + // Numerator is smaller than the divisor + modulus = c1; + return Zero; + } + if (c2.hi == 0) + { + // Divisor is a 64-bit value, so we just need one 128/64 division. + // If c1 / c2 would overflow, break c1 up into two halves. + const q1 = (c1.hi < c2.lo) ? 0 : (c1.hi / c2.lo); + if (q1) + c1.hi = c1.hi % c2.lo; + U rem; + const q0 = udivmod128_64(c1, c2.lo, rem); + modulus = Cent(rem); + return Cent(q0, q1); + } + + // Full cent precision division. + // Here c2 >= 2^^64 + // We know that c2.hi != 0, so count leading zeros is OK + // We have 0 <= shift <= 63 + const shift = (Ubits - 1) - bsr(c2.hi); + + // Normalize the divisor so its MSB is 1 + // v1 = (c2 << shift) >> 64 + U v1 = shl(c2, shift).hi; + + // To ensure no overflow. + Cent u1 = shr1(c1); + + // Get quotient from divide unsigned operation. + U rem_ignored; + const q1 = udivmod128_64(u1, v1, rem_ignored); + + // Undo normalization and division of c1 by 2. + Cent quotient = shr(shl(Cent(q1), shift), 63); + + // Make quotient correct or too small by 1 + if (tst(quotient)) + quotient = dec(quotient); + + // Now quotient is correct. + // Compute rem = c1 - (quotient * c2); + Cent rem = sub(c1, mul(quotient, c2)); + + // Check if remainder is larger than the divisor + if (uge(rem, c2)) + { + // Increment quotient + quotient = inc(quotient); + // Subtract c2 from remainder + rem = sub(rem, c2); + } + modulus = rem; + //printf("quotient "); print(quotient); + //printf("modulus "); print(modulus); + return quotient; +} + + +/**************************** + * Signed divide c1 / c2. + * Params: + * c1 = dividend + * c2 = divisor + * Returns: + * quotient c1 / c2 + */ +pure +Cent div(Cent c1, Cent c2) +{ + Cent modulus; + return divmod(c1, c2, modulus); +} + +/**************************** + * Signed divide c1 / c2. The remainder after division is stored to modulus. + * Params: + * c1 = dividend + * c2 = divisor + * modulus = set to c1 % c2 + * Returns: + * quotient c1 / c2 + */ +pure +Cent divmod(Cent c1, Cent c2, out Cent modulus) +{ + /* Muck about with the signs so we can use the unsigned divide + */ + if (cast(I)c1.hi < 0) + { + if (cast(I)c2.hi < 0) + { + Cent r = udivmod(neg(c1), neg(c2), modulus); + modulus = neg(modulus); + return r; + } + Cent r = neg(udivmod(neg(c1), c2, modulus)); + modulus = neg(modulus); + return r; + } + else if (cast(I)c2.hi < 0) + { + return neg(udivmod(c1, neg(c2), modulus)); + } + else + return udivmod(c1, c2, modulus); +} + +/**************************** + * If c1 > c2 unsigned + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 > c2 + */ +pure +bool ugt(Cent c1, Cent c2) +{ + return (c1.hi == c2.hi) ? (c1.lo > c2.lo) : (c1.hi > c2.hi); +} + +/**************************** + * If c1 >= c2 unsigned + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 >= c2 + */ +pure +bool uge(Cent c1, Cent c2) +{ + return !ugt(c2, c1); +} + +/**************************** + * If c1 < c2 unsigned + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 < c2 + */ +pure +bool ult(Cent c1, Cent c2) +{ + return ugt(c2, c1); +} + +/**************************** + * If c1 <= c2 unsigned + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 <= c2 + */ +pure +bool ule(Cent c1, Cent c2) +{ + return !ugt(c1, c2); +} + +/**************************** + * If c1 > c2 signed + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 > c2 + */ +pure +bool gt(Cent c1, Cent c2) +{ + return (c1.hi == c2.hi) + ? (c1.lo > c2.lo) + : (cast(I)c1.hi > cast(I)c2.hi); +} + +/**************************** + * If c1 >= c2 signed + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 >= c2 + */ +pure +bool ge(Cent c1, Cent c2) +{ + return !gt(c2, c1); +} + +/**************************** + * If c1 < c2 signed + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 < c2 + */ +pure +bool lt(Cent c1, Cent c2) +{ + return gt(c2, c1); +} + +/**************************** + * If c1 <= c2 signed + * Params: + * c1 = operand 1 + * c2 = operand 2 + * Returns: + * true if c1 <= c2 + */ +pure +bool le(Cent c1, Cent c2) +{ + return !gt(c1, c2); +} + +/*******************************************************/ + +version (unittest) +{ + version (none) + { + import core.stdc.stdio; + + @trusted + void print(Cent c) + { + printf("%lld, %lld\n", cast(ulong)c.lo, cast(ulong)c.hi); + printf("x%llx, x%llx\n", cast(ulong)c.lo, cast(ulong)c.hi); + } + } +} + +unittest +{ + const C0 = Zero; + const C1 = One; + const C2 = Cent(2); + const C3 = Cent(3); + const C5 = Cent(5); + const C10 = Cent(10); + const C20 = Cent(20); + const C30 = Cent(30); + const C100 = Cent(100); + + const Cm1 = neg(One); + const Cm3 = neg(C3); + const Cm10 = neg(C10); + + const C3_1 = Cent(1,3); + const C3_2 = Cent(2,3); + const C4_8 = Cent(8, 4); + const C5_0 = Cent(0, 5); + const C7_1 = Cent(1,7); + const C7_9 = Cent(9,7); + const C9_3 = Cent(3,9); + const C10_0 = Cent(0,10); + const C10_1 = Cent(1,10); + const C10_3 = Cent(3,10); + const C11_3 = Cent(3,11); + const C20_0 = Cent(0,20); + const C90_30 = Cent(30,90); + + const Cm10_0 = inc(com(C10_0)); // Cent(0, -10); + const Cm10_1 = inc(com(C10_1)); // Cent(-1, -11); + const Cm10_3 = inc(com(C10_3)); // Cent(-3, -11); + + enum Cs_3 = Cent(3, I.min); + + const Cbig_1 = Cent(0xa3ccac1832952398, 0xc3ac542864f652f8); + const Cbig_2 = Cent(0x5267b85f8a42fc20, 0); + const Cbig_3 = Cent(0xf0000000ffffffff, 0); + + /************************/ + + assert( ugt(C1, C0) ); + assert( ult(C1, C2) ); + assert( uge(C1, C0) ); + assert( ule(C1, C2) ); + + assert( !ugt(C0, C1) ); + assert( !ult(C2, C1) ); + assert( !uge(C0, C1) ); + assert( !ule(C2, C1) ); + + assert( !ugt(C1, C1) ); + assert( !ult(C1, C1) ); + assert( uge(C1, C1) ); + assert( ule(C2, C2) ); + + assert( ugt(C10_3, C10_1) ); + assert( ugt(C11_3, C10_3) ); + assert( !ugt(C9_3, C10_3) ); + assert( !ugt(C9_3, C9_3) ); + + assert( gt(C2, C1) ); + assert( !gt(C1, C2) ); + assert( !gt(C1, C1) ); + assert( gt(C0, Cm1) ); + assert( gt(Cm1, neg(C10))); + assert( !gt(Cm1, Cm1) ); + assert( !gt(Cm1, C0) ); + + assert( !lt(C2, C1) ); + assert( !le(C2, C1) ); + assert( ge(C2, C1) ); + + assert(neg(C10_0) == Cm10_0); + assert(neg(C10_1) == Cm10_1); + assert(neg(C10_3) == Cm10_3); + + assert(add(C7_1,C3_2) == C10_3); + assert(sub(C1,C2) == Cm1); + + assert(inc(C3_1) == C3_2); + assert(dec(C3_2) == C3_1); + + assert(shl(C10,0) == C10); + assert(shl(C10,Ubits) == C10_0); + assert(shl(C10,1) == C20); + assert(shl(C10,Ubits * 2) == C0); + assert(shr(C10_0,0) == C10_0); + assert(shr(C10_0,Ubits) == C10); + assert(shr(C10_0,Ubits - 1) == C20); + assert(shr(C10_0,Ubits + 1) == C5); + assert(shr(C10_0,Ubits * 2) == C0); + assert(sar(C10_0,0) == C10_0); + assert(sar(C10_0,Ubits) == C10); + assert(sar(C10_0,Ubits - 1) == C20); + assert(sar(C10_0,Ubits + 1) == C5); + assert(sar(C10_0,Ubits * 2) == C0); + assert(sar(Cm1,Ubits * 2) == Cm1); + + assert(shl1(C10) == C20); + assert(shr1(C10_0) == C5_0); + assert(sar1(C10_0) == C5_0); + assert(sar1(Cm1) == Cm1); + + Cent modulus; + + assert(udiv(C10,C2) == C5); + assert(udivmod(C10,C2, modulus) == C5); assert(modulus == C0); + assert(udivmod(C10,C3, modulus) == C3); assert(modulus == C1); + assert(udivmod(C10,C0, modulus) == Cm1); assert(modulus == C0); + assert(udivmod(C2,C90_30, modulus) == C0); assert(modulus == C2); + assert(udiv(mul(C90_30, C2), C2) == C90_30); + assert(udiv(mul(C90_30, C2), C90_30) == C2); + + assert(div(C10,C3) == C3); + assert(divmod( C10, C3, modulus) == C3); assert(modulus == C1); + assert(divmod(Cm10, C3, modulus) == Cm3); assert(modulus == Cm1); + assert(divmod( C10, Cm3, modulus) == Cm3); assert(modulus == C1); + assert(divmod(Cm10, Cm3, modulus) == C3); assert(modulus == Cm1); + assert(divmod(C2, C90_30, modulus) == C0); assert(modulus == C2); + assert(div(mul(C90_30, C2), C2) == C90_30); + assert(div(mul(C90_30, C2), C90_30) == C2); + + assert(divmod(Cbig_1, Cbig_2, modulus) == Cent(0x4496aa309d4d4a2f, U.max)); + assert(modulus == Cent(0xd83203d0fdc799b8, U.max)); + assert(udivmod(Cbig_1, Cbig_2, modulus) == Cent(0x5fe0e9bace2bedad, 2)); + assert(modulus == Cent(0x2c923125a68721f8, 0)); + assert(div(Cbig_1, Cbig_3) == Cent(0xbfa6c02b5aff8b86, U.max)); + assert(udiv(Cbig_1, Cbig_3) == Cent(0xd0b7d13b48cb350f, 0)); + + assert(mul(Cm10, C1) == Cm10); + assert(mul(C1, Cm10) == Cm10); + assert(mul(C9_3, C10) == C90_30); + assert(mul(Cs_3, C10) == C30); + assert(mul(Cm10, Cm10) == C100); + + assert( or(C4_8, C3_1) == C7_9); + assert(and(C4_8, C7_9) == C4_8); + assert(xor(C4_8, C7_9) == C3_1); + + assert(rol(Cm1, 1) == Cm1); + assert(ror(Cm1, 45) == Cm1); + assert(rol(ror(C7_9, 5), 5) == C7_9); + assert(rol(C7_9, 1) == rol1(C7_9)); + assert(ror(C7_9, 1) == ror1(C7_9)); +} + + diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index a731d6f7ae4..87c45fb2872 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d @@ -4571,7 +4571,7 @@ string debugTypeName(const(TypeInfo) ti) nothrow else return debugTypeName(ci.next); else - name = ti.classinfo.name; + name = typeid(ti).name; return name; } diff --git a/libphobos/libdruntime/core/lifetime.d b/libphobos/libdruntime/core/lifetime.d index 9a99f2da02a..091269ac9a1 100644 --- a/libphobos/libdruntime/core/lifetime.d +++ b/libphobos/libdruntime/core/lifetime.d @@ -2108,6 +2108,65 @@ private T trustedMoveImpl(T)(return scope ref T source) @trusted move(x, x); } +private enum bool hasContextPointers(T) = { + static if (__traits(isStaticArray, T)) + { + return hasContextPointers!(typeof(T.init[0])); + } + else static if (is(T == struct)) + { + import core.internal.traits : anySatisfy; + return __traits(isNested, T) || anySatisfy!(hasContextPointers, typeof(T.tupleof)); + } + else return false; +} (); + +@safe @nogc nothrow pure unittest +{ + static assert(!hasContextPointers!int); + static assert(!hasContextPointers!(void*)); + + static struct S {} + static assert(!hasContextPointers!S); + static assert(!hasContextPointers!(S[1])); + + struct Nested + { + void foo() {} + } + + static assert(hasContextPointers!Nested); + static assert(hasContextPointers!(Nested[1])); + + static struct OneLevel + { + int before; + Nested n; + int after; + } + + static assert(hasContextPointers!OneLevel); + static assert(hasContextPointers!(OneLevel[1])); + + static struct TwoLevels + { + int before; + OneLevel o; + int after; + } + + static assert(hasContextPointers!TwoLevels); + static assert(hasContextPointers!(TwoLevels[1])); + + union U + { + Nested n; + } + + // unions can have false positives, so this query ignores them + static assert(!hasContextPointers!U); +} + // target must be first-parameter, because in void-functions DMD + dip1000 allows it to take the place of a return-scope private void moveEmplaceImpl(T)(scope ref T target, return scope ref T source) { @@ -2119,9 +2178,10 @@ private void moveEmplaceImpl(T)(scope ref T target, return scope ref T source) // "Cannot move object with internal pointer unless `opPostMove` is defined."); // } + import core.internal.traits : hasElaborateAssign, isAssignable, hasElaborateMove, + hasElaborateDestructor, hasElaborateCopyConstructor; static if (is(T == struct)) { - import core.internal.traits; // Unsafe when compiling without -preview=dip1000 assert((() @trusted => &source !is &target)(), "source and target must not be identical"); @@ -2141,29 +2201,36 @@ private void moveEmplaceImpl(T)(scope ref T target, return scope ref T source) // object in order to avoid double freeing and undue aliasing static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) { - // If T is nested struct, keep original context pointer - static if (__traits(isNested, T)) - enum sz = T.sizeof - (void*).sizeof; - else - enum sz = T.sizeof; - + // If there are members that are nested structs, we must take care + // not to erase any context pointers, so we might have to recurse static if (__traits(isZeroInit, T)) + wipe(source); + else + wipe(source, ref () @trusted { return *cast(immutable(T)*) __traits(initSymbol, T).ptr; } ()); + } + } + else static if (__traits(isStaticArray, T)) + { + static if (T.length) + { + static if (!hasElaborateMove!T && + !hasElaborateDestructor!T && + !hasElaborateCopyConstructor!T) { - import core.stdc.string : memset; - () @trusted { memset(&source, 0, sz); }(); + // Single blit if no special per-instance handling is required + () @trusted + { + assert(source.ptr !is target.ptr, "source and target must not be identical"); + *cast(ubyte[T.sizeof]*) &target = *cast(ubyte[T.sizeof]*) &source; + } (); } else { - import core.stdc.string : memcpy; - () @trusted { memcpy(&source, __traits(initSymbol, T).ptr, sz); }(); + for (size_t i = 0; i < source.length; ++i) + moveEmplaceImpl(target[i], source[i]); } } } - else static if (__traits(isStaticArray, T)) - { - for (size_t i = 0; i < source.length; ++i) - moveEmplaceImpl(target[i], source[i]); - } else { // Primitive data (including pointers and arrays) or class - @@ -2258,6 +2325,13 @@ pure nothrow @nogc @system unittest f(move(ncarray)); } +//debug = PRINTF; + +debug(PRINTF) +{ + import core.stdc.stdio; +} + /// Implementation of `_d_delstruct` and `_d_delstructTrace` template _d_delstructImpl(T) { @@ -2361,3 +2435,270 @@ template _d_delstructImpl(T) assert(innerDtors == 2); assert(outerDtors == 1); } + +// issue 25552 +pure nothrow @system unittest +{ + int i; + struct Nested + { + pure nothrow @nogc: + char[1] arr; // char.init is not 0 + ~this() { ++i; } + } + + { + Nested[1] dst = void; + Nested[1] src = [Nested(['a'])]; + + moveEmplace(src, dst); + assert(i == 0); + assert(dst[0].arr == ['a']); + assert(src[0].arr == [char.init]); + assert(dst[0].tupleof[$-1] is src[0].tupleof[$-1]); + } + assert(i == 2); +} + +// issue 25552 +@safe unittest +{ + int i; + struct Nested + { + ~this() { ++i; } + } + + static struct NotNested + { + Nested n; + } + + static struct Deep + { + NotNested nn; + } + + static struct Deeper + { + NotNested[1] nn; + } + + static assert(__traits(isZeroInit, Nested)); + static assert(__traits(isZeroInit, NotNested)); + static assert(__traits(isZeroInit, Deep)); + static assert(__traits(isZeroInit, Deeper)); + + { + auto a = NotNested(Nested()); + assert(a.n.tupleof[$-1]); + auto b = move(a); + assert(b.n.tupleof[$-1]); + assert(a.n.tupleof[$-1] is b.n.tupleof[$-1]); + + auto c = Deep(NotNested(Nested())); + auto d = move(c); + assert(d.nn.n.tupleof[$-1]); + assert(c.nn.n.tupleof[$-1] is d.nn.n.tupleof[$-1]); + + auto e = Deeper([NotNested(Nested())]); + auto f = move(e); + assert(f.nn[0].n.tupleof[$-1]); + assert(e.nn[0].n.tupleof[$-1] is f.nn[0].n.tupleof[$-1]); + } + assert(i == 6); +} + +// issue 25552 +@safe unittest +{ + int i; + struct Nested + { + align(32) // better still find context pointer correctly! + int[3] stuff = [0, 1, 2]; + ~this() { ++i; } + } + + static struct NoAssign + { + int value; + @disable void opAssign(typeof(this)); + } + + static struct NotNested + { + int before = 42; + align(Nested.alignof * 4) // better still find context pointer correctly! + Nested n; + auto after = NoAssign(43); + } + + static struct Deep + { + NotNested nn; + } + + static struct Deeper + { + NotNested[1] nn; + } + + static assert(!__traits(isZeroInit, Nested)); + static assert(!__traits(isZeroInit, NotNested)); + static assert(!__traits(isZeroInit, Deep)); + static assert(!__traits(isZeroInit, Deeper)); + + { + auto a = NotNested(1, Nested([3, 4, 5]), NoAssign(2)); + auto b = move(a); + assert(b.n.tupleof[$-1]); + assert(a.n.tupleof[$-1] is b.n.tupleof[$-1]); + assert(a.n.stuff == [0, 1, 2]); + assert(a.before == 42); + assert(a.after == NoAssign(43)); + + auto c = Deep(NotNested(1, Nested([3, 4, 5]), NoAssign(2))); + auto d = move(c); + assert(d.nn.n.tupleof[$-1]); + assert(c.nn.n.tupleof[$-1] is d.nn.n.tupleof[$-1]); + assert(c.nn.n.stuff == [0, 1, 2]); + assert(c.nn.before == 42); + assert(c.nn.after == NoAssign(43)); + + auto e = Deeper([NotNested(1, Nested([3, 4, 5]), NoAssign(2))]); + auto f = move(e); + assert(f.nn[0].n.tupleof[$-1]); + assert(e.nn[0].n.tupleof[$-1] is f.nn[0].n.tupleof[$-1]); + assert(e.nn[0].n.stuff == [0, 1, 2]); + assert(e.nn[0].before == 42); + assert(e.nn[0].after == NoAssign(43)); + } + assert(i == 6); +} + +// wipes source after moving +pragma(inline, true) +private void wipe(T, Init...)(return scope ref T source, ref const scope Init initializer) @trusted +if (!Init.length || + ((Init.length == 1) && (is(immutable T == immutable Init[0])))) +{ + static if (__traits(isStaticArray, T) && hasContextPointers!T) + { + for (auto i = 0; i < T.length; i++) + static if (Init.length) + wipe(source[i], initializer[0][i]); + else + wipe(source[i]); + } + else static if (is(T == struct) && hasContextPointers!T) + { + import core.internal.traits : anySatisfy; + static if (anySatisfy!(hasContextPointers, typeof(T.tupleof))) + { + static foreach (i; 0 .. T.tupleof.length - __traits(isNested, T)) + static if (Init.length) + wipe(source.tupleof[i], initializer[0].tupleof[i]); + else + wipe(source.tupleof[i]); + } + else + { + static if (__traits(isNested, T)) + enum sz = T.tupleof[$-1].offsetof; + else + enum sz = T.sizeof; + + static if (Init.length) + *cast(ubyte[sz]*) &source = *cast(ubyte[sz]*) &initializer[0]; + else + *cast(ubyte[sz]*) &source = 0; + } + } + else + { + import core.internal.traits : hasElaborateAssign, isAssignable; + static if (Init.length) + { + static if (hasElaborateAssign!T || !isAssignable!T) + *cast(ubyte[T.sizeof]*) &source = *cast(ubyte[T.sizeof]*) &initializer[0]; + else + source = *cast(T*) &initializer[0]; + } + else + { + *cast(ubyte[T.sizeof]*) &source = 0; + } + } +} + +/** + * Allocate an exception of type `T` from the exception pool and call its constructor. + * It has the same interface as `rt.lifetime._d_newclass()`. + * `T` must be Throwable or derived from it, must declare an explicit ctor + * and cannot be a COM or C++ class. + * Returns: + * constructed instance of the type + */ +T _d_newThrowable(T, Args...)(auto ref Args args) @trusted + if (is(T : Throwable) && is(typeof(T.__ctor(forward!args))) && + __traits(getLinkage, T) == "D") +{ + debug(PRINTF) printf("_d_newThrowable(%s)\n", cast(char*) T.stringof); + + import core.stdc.stdlib : malloc; + auto init = __traits(initSymbol, T); + void* p = malloc(init.length); + if (!p) + { + import core.exception : onOutOfMemoryError; + onOutOfMemoryError(); + } + + debug(PRINTF) printf(" p = %p\n", p); + + // initialize it + p[0 .. init.length] = init[]; + + import core.internal.traits : hasIndirections; + if (hasIndirections!T) + { + // Inform the GC about the pointers in the object instance + import core.memory : GC; + GC.addRange(p, init.length); + } + + debug(PRINTF) printf("initialization done\n"); + + (cast(Throwable) p).refcount() = 1; + + auto t = cast(T) p; + t.__ctor(forward!args); + + return t; +} + +@system unittest +{ + class E : Exception + { + int x; + + this(int x, string msg = "", Throwable nextInChain = null) + { + super(msg, nextInChain); + this.x = x; + } + } + + auto exc = _d_newThrowable!Exception("Exception"); + assert(exc.refcount() == 1); + assert(exc.msg == "Exception"); + + static assert(!__traits(compiles, _d_newThrowable!E())); + + auto e = _d_newThrowable!E(42, "E", null); + assert(e.refcount() == 1); + assert(e.x == 42); + assert(e.msg == "E"); +} diff --git a/libphobos/libdruntime/core/memory.d b/libphobos/libdruntime/core/memory.d index c4df0f2d0dd..6ba569a241c 100644 --- a/libphobos/libdruntime/core/memory.d +++ b/libphobos/libdruntime/core/memory.d @@ -459,8 +459,11 @@ extern(C): * Throws: * OutOfMemoryError on allocation failure. */ - pragma(mangle, "gc_malloc") static void* malloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow; - + version (D_ProfileGC) + pragma(mangle, "gc_mallocTrace") static void* malloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null, + string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow; + else + pragma(mangle, "gc_malloc") static void* malloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow; /** * Requests an aligned block of managed memory from the garbage collector. @@ -482,7 +485,11 @@ extern(C): * Throws: * OutOfMemoryError on allocation failure. */ - pragma(mangle, "gc_qalloc") static BlkInfo qalloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow; + version (D_ProfileGC) + pragma(mangle, "gc_qallocTrace") static BlkInfo qalloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null, + string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow; + else + pragma(mangle, "gc_qalloc") static BlkInfo qalloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow; /** @@ -506,7 +513,11 @@ extern(C): * Throws: * OutOfMemoryError on allocation failure. */ - pragma(mangle, "gc_calloc") static void* calloc(size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow; + version (D_ProfileGC) + pragma(mangle, "gc_callocTrace") static void* calloc(size_t sz, uint ba = 0, const TypeInfo ti = null, + string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow; + else + pragma(mangle, "gc_calloc") static void* calloc(size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow; /** @@ -551,7 +562,11 @@ extern(C): * Throws: * `OutOfMemoryError` on allocation failure. */ - pragma(mangle, "gc_realloc") static void* realloc(return scope void* p, size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow; + version (D_ProfileGC) + pragma(mangle, "gc_reallocTrace") static void* realloc(return scope void* p, size_t sz, uint ba = 0, const TypeInfo ti = null, + string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow; + else + pragma(mangle, "gc_realloc") static void* realloc(return scope void* p, size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow; // https://issues.dlang.org/show_bug.cgi?id=13111 /// @@ -593,7 +608,12 @@ extern(C): * as an indicator of success. $(LREF capacity) should be used to * retrieve actual usable slice capacity. */ - pragma(mangle, "gc_extend") static size_t extend(void* p, size_t mx, size_t sz, const TypeInfo ti = null) pure nothrow; + version (D_ProfileGC) + pragma(mangle, "gc_extendTrace") static size_t extend(void* p, size_t mx, size_t sz, const TypeInfo ti = null, + string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow; + else + pragma(mangle, "gc_extend") static size_t extend(void* p, size_t mx, size_t sz, const TypeInfo ti = null) pure nothrow; + /// Standard extending unittest { diff --git a/libphobos/libdruntime/core/stdc/math.d b/libphobos/libdruntime/core/stdc/math.d index de029c41af8..0c5da0bfefa 100644 --- a/libphobos/libdruntime/core/stdc/math.d +++ b/libphobos/libdruntime/core/stdc/math.d @@ -850,59 +850,59 @@ else version (CRuntime_UClibc) FP_FAST_FMAL = 0, } - int __fpclassifyf(float x); - int __fpclassify(double x); - int __fpclassifyl(real x); + pure int __fpclassifyf(float x); + pure int __fpclassify(double x); + pure int __fpclassifyl(real x); - int __finitef(float x); - int __finite(double x); - int __finitel(real x); + pure int __finitef(float x); + pure int __finite(double x); + pure int __finitel(real x); - int __isinff(float x); - int __isinf(double x); - int __isinfl(real x); + pure int __isinff(float x); + pure int __isinf(double x); + pure int __isinfl(real x); - int __isnanf(float x); - int __isnan(double x); - int __isnanl(real x); + pure int __isnanf(float x); + pure int __isnan(double x); + pure int __isnanl(real x); - int __signbitf(float x); - int __signbit(double x); - int __signbitl(real x); + pure int __signbitf(float x); + pure int __signbit(double x); + pure int __signbitl(real x); /// - pragma(mangle, "__fpclassifyf") int fpclassify(float x); + pragma(mangle, "__fpclassifyf") pure int fpclassify(float x); /// - pragma(mangle, "__fpclassify") int fpclassify(double x); + pragma(mangle, "__fpclassify") pure int fpclassify(double x); /// pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl") - int fpclassify(real x); + pure int fpclassify(real x); /// - pragma(mangle, "__finitef") int isfinite(float x); + pragma(mangle, "__finitef") pure int isfinite(float x); /// - pragma(mangle, "__finite") int isfinite(double x); + pragma(mangle, "__finite") pure int isfinite(double x); /// pragma(mangle, real.sizeof == double.sizeof ? "__finite" : "__finitel") - int isfinite(real x); + pure int isfinite(real x); /// - pragma(mangle, "__isinff") int isinf(float x); + pragma(mangle, "__isinff") pure int isinf(float x); /// - pragma(mangle, "__isinf") int isinf(double x); + pragma(mangle, "__isinf") pure int isinf(double x); /// pragma(mangle, real.sizeof == double.sizeof ? "__isinf" : "__isinfl") - int isinf(real x); + pure int isinf(real x); /// - pragma(mangle, "__isnanf") int isnan(float x); + pragma(mangle, "__isnanf") pure int isnan(float x); /// - pragma(mangle, "__isnan") int isnan(double x); + pragma(mangle, "__isnan") pure int isnan(double x); /// pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl") - int isnan(real x); + pure int isnan(real x); - extern (D) + extern (D) pure { /// int isnormal(float x) { return fpclassify(x) == FP_NORMAL; } @@ -913,12 +913,12 @@ else version (CRuntime_UClibc) } /// - pragma(mangle, "__signbitf") int signbit(float x); + pragma(mangle, "__signbitf") pure int signbit(float x); /// - pragma(mangle, "__signbit") int signbit(double x); + pragma(mangle, "__signbit") pure int signbit(double x); /// pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl") - int signbit(real x); + pure int signbit(real x); } else version (Darwin) { @@ -3089,7 +3089,7 @@ else version (OpenBSD) /// pure real atanl(real x); /// - real atan2l(real x, real y); + real atan2l(real y, real x); /// pure real cosl(real x); /// @@ -3377,7 +3377,7 @@ else version (DragonFlyBSD) pure real acosl(real x); pure real asinl(real x); pure real atanl(real x); - real atan2l(real x, real y); + real atan2l(real y, real x); pure real cosl(real x); pure real sinl(real x); pure real tanl(real x); @@ -3872,7 +3872,7 @@ else version (CRuntime_UClibc) /// float atan2f(float y, float x); /// - extern(D) real atan2l(real y, real x) { return atan2(cast(double) x, cast(double) y); } + extern(D) real atan2l(real y, real x) { return atan2(cast(double) y, cast(double) x); } /// pure double cos(double x); diff --git a/libphobos/libdruntime/core/sys/darwin/dlfcn.d b/libphobos/libdruntime/core/sys/darwin/dlfcn.d index 406d588abf3..6084f3f01f1 100644 --- a/libphobos/libdruntime/core/sys/darwin/dlfcn.d +++ b/libphobos/libdruntime/core/sys/darwin/dlfcn.d @@ -25,20 +25,6 @@ nothrow: public import core.sys.posix.dlfcn; -struct Dl_info -{ - const(char)* dli_fname; - void* dli_fbase; - const(char)* dli_sname; - void* dli_saddr; -} - -int dladdr(const scope void* addr, Dl_info* info); - -enum RTLD_NOLOAD = 0x10; -enum RTLD_NODELETE = 0x80; -enum RTLD_FIRST = 0x100; - enum RTLD_NEXT = cast(void*) -1; enum RTLD_DEFAULT = cast(void*) -2; enum RTLD_SELF = cast(void*) -3; diff --git a/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d b/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d index 2c5d8d79c22..0e8b15ccf88 100644 --- a/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d @@ -15,18 +15,6 @@ public import core.sys.posix.dlfcn; extern (C) nothrow @nogc @system: /* - * Modes and flags for dlopen(). - */ -static assert(RTLD_LAZY == 1); -static assert(RTLD_NOW == 2); -enum RTLD_MODEMASK = 0x3; -static assert(RTLD_GLOBAL == 0x100); -static assert(RTLD_LOCAL == 0); -enum RTLD_TRACE = 0x200; -enum RTLD_NODELETE = 0x01000; -enum RTLD_NOLOAD = 0x02000; - -/* * Request arguments for dlinfo(). */ enum RTLD_DI_LINKMAP = 2; /* Obtain link map. */ @@ -43,17 +31,6 @@ enum RTLD_DEFAULT = cast(void *)-2; /* Use default search algorithm. */ enum RTLD_SELF = cast(void *)-3; /* Search the caller itself. */ /* - * Structure filled in by dladdr(). - */ -struct Dl_info { - const(char) *dli_fname; /* Pathname of shared object. */ - void *dli_fbase; /* Base address of shared object. */ - const(char) *dli_sname; /* Name of nearest symbol. */ - void *dli_saddr; /* Address of nearest symbol. */ -} - - -/* * Structures, returned by the RTLD_DI_SERINFO dlinfo() request. */ struct Dl_serpath { @@ -91,7 +68,6 @@ extern(C) { } void* fdlopen(int, int); -int dladdr(const(void)*, Dl_info*); dlfunc_t dlfunc(void*, const(char)*); int dlinfo(void*, int, void*); /*void dllockinit(void* _context, diff --git a/libphobos/libdruntime/core/sys/freebsd/dlfcn.d b/libphobos/libdruntime/core/sys/freebsd/dlfcn.d index 7baacfeeb7b..aac41d8e7d6 100644 --- a/libphobos/libdruntime/core/sys/freebsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/freebsd/dlfcn.d @@ -17,18 +17,6 @@ nothrow: enum __BSD_VISIBLE = true; /* - * Modes and flags for dlopen(). - */ -static assert(RTLD_LAZY == 1); -static assert(RTLD_NOW == 2); -enum RTLD_MODEMASK = 0x3; -static assert(RTLD_GLOBAL == 0x100); -static assert(RTLD_LOCAL == 0); -enum RTLD_TRACE = 0x200; -enum RTLD_NODELETE = 0x01000; -enum RTLD_NOLOAD = 0x02000; - -/* * Request arguments for dlinfo(). */ enum RTLD_DI_LINKMAP = 2; /* Obtain link map. */ @@ -46,16 +34,6 @@ enum RTLD_SELF = cast(void *)-3; /* Search the caller itself. */ static if (__BSD_VISIBLE) { - /* - * Structure filled in by dladdr(). - */ - struct Dl_info { - const(char) *dli_fname; /* Pathname of shared object. */ - void *dli_fbase; /* Base address of shared object. */ - const(char) *dli_sname; /* Name of nearest symbol. */ - void *dli_saddr; /* Address of nearest symbol. */ - } - /*- * The actual type declared by this typedef is immaterial, provided that * it is a function pointer. Its purpose is to provide a return type for @@ -97,7 +75,6 @@ extern(C) { static if (__BSD_VISIBLE) { void* fdlopen(int, int); - int dladdr(const(void)*, Dl_info*); dlfunc_t dlfunc(void*, const(char)*); int dlinfo(void*, int, void*); void dllockinit(void* _context, diff --git a/libphobos/libdruntime/core/sys/linux/dlfcn.d b/libphobos/libdruntime/core/sys/linux/dlfcn.d index a815d0907fe..4a122849471 100644 --- a/libphobos/libdruntime/core/sys/linux/dlfcn.d +++ b/libphobos/libdruntime/core/sys/linux/dlfcn.d @@ -34,16 +34,6 @@ import core.sys.linux.config; version (X86_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h - // enum RTLD_LAZY = 0x00001; // POSIX - // enum RTLD_NOW = 0x00002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00004; - enum RTLD_DEEPBIND = 0x00008; - - // enum RTLD_GLOBAL = 0x00100; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -58,16 +48,6 @@ version (X86_Any) else version (HPPA_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=ports/sysdeps/hppa/bits/dlfcn.h - // enum RTLD_LAZY = 0x0001; // POSIX - // enum RTLD_NOW = 0x0002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00004; - enum RTLD_DEEPBIND = 0x00008; - - // enum RTLD_GLOBAL = 0x0004; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -82,16 +62,6 @@ else version (HPPA_Any) else version (MIPS_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=ports/sysdeps/mips/bits/dlfcn.h - // enum RTLD_LAZY = 0x0001; // POSIX - // enum RTLD_NOW = 0x0002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00008; - enum RTLD_DEEPBIND = 0x00010; - - // enum RTLD_GLOBAL = 0x0004; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -106,16 +76,6 @@ else version (MIPS_Any) else version (PPC_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h - // enum RTLD_LAZY = 0x0001; // POSIX - // enum RTLD_NOW = 0x0002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00004; - enum RTLD_DEEPBIND = 0x00008; - - // enum RTLD_GLOBAL = 0x00100; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -130,16 +90,6 @@ else version (PPC_Any) else version (ARM_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h - // enum RTLD_LAZY = 0x0001; // POSIX - // enum RTLD_NOW = 0x0002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00004; - enum RTLD_DEEPBIND = 0x00008; - - // enum RTLD_GLOBAL = 0x00100; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -154,16 +104,6 @@ else version (ARM_Any) else version (RISCV_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h - // enum RTLD_LAZY = 0x0001; // POSIX - // enum RTLD_NOW = 0x0002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00004; - enum RTLD_DEEPBIND = 0x00008; - - // enum RTLD_GLOBAL = 0x00100; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -178,16 +118,6 @@ else version (RISCV_Any) else version (SPARC_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h - // enum RTLD_LAZY = 0x0001; // POSIX - // enum RTLD_NOW = 0x0002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00004; - enum RTLD_DEEPBIND = 0x00008; - - // enum RTLD_GLOBAL = 0x00100; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -202,16 +132,6 @@ else version (SPARC_Any) else version (IBMZ_Any) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h - // enum RTLD_LAZY = 0x0001; // POSIX - // enum RTLD_NOW = 0x0002; // POSIX - enum RTLD_BINDING_MASK = 0x3; - enum RTLD_NOLOAD = 0x00004; - enum RTLD_DEEPBIND = 0x00008; - - // enum RTLD_GLOBAL = 0x00100; // POSIX - // enum RTLD_LOCAL = 0; // POSIX - enum RTLD_NODELETE = 0x01000; - static if (__USE_GNU) { RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) @@ -251,15 +171,6 @@ static if (__USE_GNU) static if (__USE_GNU) { - struct Dl_info - { - const(char)* dli_fname; - void* dli_fbase; - const(char)* dli_sname; - void* dli_saddr; - } - - int dladdr(const scope void* __address, Dl_info* __info); int dladdr1(void* __address, Dl_info* __info, void** __extra_info, int __flags); enum diff --git a/libphobos/libdruntime/core/sys/linux/sys/inotify.d b/libphobos/libdruntime/core/sys/linux/sys/inotify.d index e0acf33fa51..11bdc85cf78 100644 --- a/libphobos/libdruntime/core/sys/linux/sys/inotify.d +++ b/libphobos/libdruntime/core/sys/linux/sys/inotify.d @@ -6,7 +6,21 @@ */ module core.sys.linux.sys.inotify; -version (linux): +// The BSDs (including macOS) have a kqueue-backed API-compatible inotify +// library in ports. However, inotify is a Linux interface so it lives here. +// All BSD people need this library to use inotify: +// https://github.com/libinotify-kqueue/libinotify-kqueue +// It is the responsibility of all BSD people to configure the library before +// using this interface. + +version (linux) version = LinuxOrCompatible; +version (Darwin) version = LinuxOrCompatible; +version (FreeBSD) version = LinuxOrCompatible; +version (OpenBSD) version = LinuxOrCompatible; +version (NetBSD) version = LinuxOrCompatible; +version (DragonFlyBSD) version = LinuxOrCompatible; + +version (LinuxOrCompatible): extern (C): @system: nothrow: diff --git a/libphobos/libdruntime/core/sys/netbsd/dlfcn.d b/libphobos/libdruntime/core/sys/netbsd/dlfcn.d index 468ffbfe435..3785592fcd6 100644 --- a/libphobos/libdruntime/core/sys/netbsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/netbsd/dlfcn.d @@ -19,17 +19,6 @@ nothrow: enum __BSD_VISIBLE = true; /* - * Modes and flags for dlopen(). - */ -static assert(RTLD_LAZY == 1); -static assert(RTLD_NOW == 2); -static assert(RTLD_GLOBAL == 0x100); -static assert(RTLD_LOCAL == 0x200); -//enum RTLD_TRACE = 0x200; -enum RTLD_NODELETE = 0x01000; -enum RTLD_NOLOAD = 0x02000; - -/* * Request arguments for dlinfo(). */ enum RTLD_DI_LINKMAP = 3; /* Obtain link map. */ @@ -47,16 +36,6 @@ enum RTLD_SELF = cast(void *)-3; /* Search the caller itself. */ static if (__BSD_VISIBLE) { - /* - * Structure filled in by dladdr(). - */ - struct Dl_info { - const(char) *dli_fname; /* Pathname of shared object. */ - void *dli_fbase; /* Base address of shared object. */ - const(char) *dli_sname; /* Name of nearest symbol. */ - void *dli_saddr; /* Address of nearest symbol. */ - } - /*- * The actual type declared by this typedef is immaterial, provided that * it is a function pointer. Its purpose is to provide a return type for @@ -97,16 +76,6 @@ extern(C) { static if (__BSD_VISIBLE) { - //void* fdlopen(int, int); - int dladdr(const(void)*, Dl_info*); - //dlfunc_t dlfunc(void*, const(char)*); int dlinfo(void*, int, void*); - /+void dllockinit(void* _context, - void* function(void* _context) _lock_create, - void function(void* _lock) _rlock_acquire, - void function(void* _lock) _wlock_acquire, - void function(void* _lock) _lock_release, - void function(void* _lock) _lock_destroy, - void function(void* _context) _context_destroy);+/ void* dlvsym(void*, const(char)*, const(char)*); } diff --git a/libphobos/libdruntime/core/sys/openbsd/dlfcn.d b/libphobos/libdruntime/core/sys/openbsd/dlfcn.d index b28dc63d5c7..1227669e624 100644 --- a/libphobos/libdruntime/core/sys/openbsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/openbsd/dlfcn.d @@ -11,13 +11,6 @@ version (OpenBSD): extern (C): nothrow: -static assert(RTLD_LAZY == 1); -static assert(RTLD_NOW == 2); -static assert(RTLD_GLOBAL == 0x100); -static assert(RTLD_LOCAL == 0); -enum RTLD_TRACE = 0x200; -enum RTLD_NODELETE = 0x400; - enum RTLD_NEXT = cast(void *)-1; enum RTLD_DEFAULT = cast(void *)-2; enum RTLD_SELF = cast(void *)-3; diff --git a/libphobos/libdruntime/core/sys/posix/dirent.d b/libphobos/libdruntime/core/sys/posix/dirent.d index 8a2440e1fa4..bffbc5149a1 100644 --- a/libphobos/libdruntime/core/sys/posix/dirent.d +++ b/libphobos/libdruntime/core/sys/posix/dirent.d @@ -37,12 +37,124 @@ nothrow: // Required // /* -DIR - struct dirent { char[] d_name; } +*/ + +version (linux) +{ + struct dirent + { + ino_t d_ino; + off_t d_off; + ushort d_reclen; + ubyte d_type; + char[256] d_name = 0; + } +} +else version (Darwin) +{ + // _DARWIN_FEATURE_64_BIT_INODE dirent is default for Mac OSX >10.5 and is + // only meaningful type for other OS X/Darwin variants (e.g. iOS). + // man dir(5) has some info, man stat(2) gives details. + struct dirent + { + ino_t d_ino; + alias d_fileno = d_ino; + ulong d_seekoff; + ushort d_reclen; + ushort d_namlen; + ubyte d_type; + char[1024] d_name = 0; + } +} +else version (FreeBSD) +{ + import core.sys.freebsd.config; + + static if (__FreeBSD_version >= 1200000) + { + struct dirent + { + ino_t d_fileno; + off_t d_off; + ushort d_reclen; + ubyte d_type; + ubyte d_pad0; + ushort d_namlen; + ushort d_pad1; + char[256] d_name = 0; + } + } + else + { + align(4) + struct dirent + { + uint d_fileno; + ushort d_reclen; + ubyte d_type; + ubyte d_namlen; + char[256] d_name = 0; + } + } +} +else version (NetBSD) +{ + struct dirent + { + ulong d_fileno; + ushort d_reclen; + ushort d_namlen; + ubyte d_type; + char[512] d_name = 0; + } +} +else version (OpenBSD) +{ + align(4) + struct dirent + { + ino_t d_fileno; + off_t d_off; + ushort d_reclen; + ubyte d_type; + ubyte d_namlen; + ubyte[4] __d_padding; + char[256] d_name = 0; + } +} +else version (DragonFlyBSD) +{ + struct dirent + { + ino_t d_fileno; /* file number of entry */ + ushort d_reclen; /* strlen(d_name) */ + ubyte d_type; /* file type, see blow */ + ubyte d_unused1; /* padding, reserved */ + uint d_unused2; /* reserved */ + char[256] d_name = 0; /* name, NUL-terminated */ + } +} +else version (Solaris) +{ + struct dirent + { + ino_t d_ino; + off_t d_off; + ushort d_reclen; + char[1] d_name = 0; + } +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +DIR int closedir(DIR*); DIR* opendir(const scope char*); @@ -67,15 +179,6 @@ version (CRuntime_Glibc) DT_WHT = 14 } - struct dirent - { - ino_t d_ino; - off_t d_off; - ushort d_reclen; - ubyte d_type; - char[256] d_name = 0; - } - struct DIR { // Managed by OS @@ -106,20 +209,6 @@ else version (Darwin) DT_WHT = 14 } - // _DARWIN_FEATURE_64_BIT_INODE dirent is default for Mac OSX >10.5 and is - // only meaningful type for other OS X/Darwin variants (e.g. iOS). - // man dir(5) has some info, man stat(2) gives details. - struct dirent - { - ino_t d_ino; - alias d_fileno = d_ino; - ulong d_seekoff; - ushort d_reclen; - ushort d_namlen; - ubyte d_type; - char[1024] d_name = 0; - } - struct DIR { // Managed by OS @@ -157,33 +246,6 @@ else version (FreeBSD) DT_WHT = 14 } - static if (__FreeBSD_version >= 1200000) - { - struct dirent - { - ino_t d_fileno; - off_t d_off; - ushort d_reclen; - ubyte d_type; - ubyte d_pad0; - ushort d_namlen; - ushort d_pad1; - char[256] d_name = 0; - } - } - else - { - align(4) - struct dirent - { - uint d_fileno; - ushort d_reclen; - ubyte d_type; - ubyte d_namlen; - char[256] d_name = 0; - } - } - alias void* DIR; version (GNU) @@ -213,15 +275,6 @@ else version (NetBSD) DT_WHT = 14 } - struct dirent - { - ulong d_fileno; - ushort d_reclen; - ushort d_namlen; - ubyte d_type; - char[512] d_name = 0; - } - alias void* DIR; dirent* __readdir30(DIR*); @@ -241,18 +294,6 @@ else version (OpenBSD) DT_SOCK = 12, } - align(4) - struct dirent - { - ino_t d_fileno; - off_t d_off; - ushort d_reclen; - ubyte d_type; - ubyte d_namlen; - ubyte[4] __d_padding; - char[256] d_name = 0; - } - alias void* DIR; dirent* readdir(DIR*); @@ -273,30 +314,12 @@ else version (DragonFlyBSD) DT_DBF = 15, /* database record file */ } - struct dirent - { - ino_t d_fileno; /* file number of entry */ - ushort d_reclen; /* strlen(d_name) */ - ubyte d_type; /* file type, see blow */ - ubyte d_unused1; /* padding, reserved */ - uint d_unused2; /* reserved */ - char[256] d_name = 0; /* name, NUL-terminated */ - } - alias void* DIR; dirent* readdir(DIR*); } else version (Solaris) { - struct dirent - { - ino_t d_ino; - off_t d_off; - ushort d_reclen; - char[1] d_name = 0; - } - struct DIR { int dd_fd; @@ -338,15 +361,6 @@ else version (CRuntime_Bionic) DT_WHT = 14 } - struct dirent - { - ulong d_ino; - long d_off; - ushort d_reclen; - ubyte d_type; - char[256] d_name = 0; - } - struct DIR { } @@ -368,15 +382,6 @@ else version (CRuntime_Musl) DT_WHT = 14 } - struct dirent - { - ino_t d_ino; - off_t d_off; - ushort d_reclen; - ubyte d_type; - char[256] d_name = 0; - } - struct DIR { } @@ -408,23 +413,6 @@ else version (CRuntime_UClibc) DT_WHT = 14 } - struct dirent - { - static if (__USE_FILE_OFFSET64) - { - ino64_t d_ino; - off64_t d_off; - } - else - { - ino_t d_ino; - off_t d_off; - } - ushort d_reclen; - ubyte d_type; - char[256] d_name = 0; - } - struct DIR { // Managed by OS diff --git a/libphobos/libdruntime/core/sys/posix/dlfcn.d b/libphobos/libdruntime/core/sys/posix/dlfcn.d index f6476ec3106..a9519ca234a 100644 --- a/libphobos/libdruntime/core/sys/posix/dlfcn.d +++ b/libphobos/libdruntime/core/sys/posix/dlfcn.d @@ -66,59 +66,100 @@ version (CRuntime_Glibc) { version (X86_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h enum RTLD_LAZY = 0x00001; enum RTLD_NOW = 0x00002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x00100; enum RTLD_LOCAL = 0x00000; + enum RTLD_NODELETE = 0x01000; } else version (HPPA_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=ports/sysdeps/hppa/bits/dlfcn.h enum RTLD_LAZY = 0x0001; enum RTLD_NOW = 0x0002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x0100; enum RTLD_LOCAL = 0; + enum RTLD_NODELETE = 0x01000; } else version (MIPS_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=ports/sysdeps/mips/bits/dlfcn.h enum RTLD_LAZY = 0x0001; enum RTLD_NOW = 0x0002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00008; + enum RTLD_DEEPBIND = 0x00010; enum RTLD_GLOBAL = 0x0004; enum RTLD_LOCAL = 0; + enum RTLD_NODELETE = 0x01000; } else version (PPC_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h enum RTLD_LAZY = 0x00001; enum RTLD_NOW = 0x00002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x00100; enum RTLD_LOCAL = 0; + enum RTLD_NODELETE = 0x01000; } else version (ARM_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h enum RTLD_LAZY = 0x00001; enum RTLD_NOW = 0x00002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x00100; enum RTLD_LOCAL = 0; + enum RTLD_NODELETE = 0x01000; } else version (RISCV_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h enum RTLD_LAZY = 0x00001; enum RTLD_NOW = 0x00002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x00100; enum RTLD_LOCAL = 0; + enum RTLD_NODELETE = 0x01000; } else version (SPARC_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h enum RTLD_LAZY = 0x00001; enum RTLD_NOW = 0x00002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x00100; enum RTLD_LOCAL = 0; + enum RTLD_NODELETE = 0x01000; + } else version (IBMZ_Any) { + // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h enum RTLD_LAZY = 0x00001; enum RTLD_NOW = 0x00002; + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x00100; enum RTLD_LOCAL = 0; + enum RTLD_NODELETE = 0x01000; } else static assert(0, "unimplemented"); @@ -127,13 +168,25 @@ version (CRuntime_Glibc) char* dlerror(); void* dlopen(const scope char*, int); void* dlsym(void*, const scope char*); + int dladdr(const scope void*, Dl_info*); + + struct Dl_info + { + const(char)* dli_fname; + void* dli_fbase; + const(char)* dli_sname; + void* dli_saddr; + } } else version (Darwin) { enum RTLD_LAZY = 0x00001; enum RTLD_NOW = 0x00002; + enum RTLD_NOLOAD = 0x10; + enum RTLD_NODELETE = 0x80; enum RTLD_GLOBAL = 0x00100; enum RTLD_LOCAL = 0x00000; + enum RTLD_FIRST = 0x100; int dlclose(void*); char* dlerror(); @@ -153,8 +206,12 @@ else version (FreeBSD) { enum RTLD_LAZY = 1; enum RTLD_NOW = 2; + enum RTLD_MODEMASK = 0x3; enum RTLD_GLOBAL = 0x100; enum RTLD_LOCAL = 0; + enum RTLD_TRACE = 0x200; + enum RTLD_NODELETE = 0x01000; + enum RTLD_NOLOAD = 0x02000; int dlclose(void*); char* dlerror(); @@ -199,6 +256,8 @@ else version (OpenBSD) enum RTLD_NOW = 2; enum RTLD_GLOBAL = 0x100; enum RTLD_LOCAL = 0; + enum RTLD_TRACE = 0x200; + enum RTLD_NODELETE = 0x400; int dlclose(void*); char* dlerror(); @@ -218,8 +277,12 @@ else version (DragonFlyBSD) { enum RTLD_LAZY = 1; enum RTLD_NOW = 2; + enum RTLD_MODEMASK = 0x3; enum RTLD_GLOBAL = 0x100; enum RTLD_LOCAL = 0; + enum RTLD_TRACE = 0x200; + enum RTLD_NODELETE = 0x01000; + enum RTLD_NOLOAD = 0x02000; int dlclose(void*); char* dlerror(); @@ -239,8 +302,16 @@ else version (Solaris) { enum RTLD_LAZY = 1; enum RTLD_NOW = 2; + enum RTLD_NOLOAD = 0x00004; + enum RTLD_DEEPBIND = 0x00008; enum RTLD_GLOBAL = 0x100; enum RTLD_LOCAL = 0; + enum RTLD_PARENT = 0x00200; + enum RTLD_GROUP = 0x00400; + enum RTLD_WORLD = 0x00800; + enum RTLD_NODELETE = 0x01000; + enum RTLD_FIRST = 0x02000; + enum RTLD_CONFGEN = 0x10000; int dlclose(void*); char* dlerror(); @@ -343,4 +414,13 @@ else version (CRuntime_UClibc) char* dlerror(); void* dlopen(const scope char*, int); void* dlsym(void*, const scope char*); + int dladdr(const scope void*, Dl_info*); + + struct Dl_info + { + const(char)* dli_fname; + void* dli_fbase; + const(char)* dli_sname; + void* dli_saddr; + } } diff --git a/libphobos/libdruntime/core/sys/posix/fcntl.d b/libphobos/libdruntime/core/sys/posix/fcntl.d index 6833f3badf0..3c196d29f22 100644 --- a/libphobos/libdruntime/core/sys/posix/fcntl.d +++ b/libphobos/libdruntime/core/sys/posix/fcntl.d @@ -96,12 +96,8 @@ struct flock off_t l_len; pid_t l_pid; } - -int creat(const scope char*, mode_t); -int fcntl(int, int, ...); -int open(const scope char*, int, ...); */ -version (CRuntime_Glibc) +version (linux) { enum F_DUPFD = 0; enum F_GETFD = 1; @@ -121,6 +117,12 @@ version (CRuntime_Glibc) enum F_SETLK = 6; enum F_SETLKW = 7; } + else version (PPC64) + { + enum F_GETLK = 5; + enum F_SETLK = 6; + enum F_SETLKW = 7; + } else version (SystemZ) { static assert(off_t.sizeof == 8); @@ -163,6 +165,19 @@ version (CRuntime_Glibc) enum O_SYNC = 0x101000; // octal 04010000 enum O_DSYNC = 0x1000; // octal 010000 enum O_RSYNC = O_SYNC; + + enum O_DIRECTORY = 0x010000; // octal 0200000 + enum O_NOFOLLOW = 0x020000; // octal 0400000 + enum O_DIRECT = 0x004000; // octal 040000 + version (X86_64) + enum O_LARGEFILE = 0; + else + enum O_LARGEFILE = 0x08000; // octal 0100000 + enum O_TMPFILE = 0x410000; // octal 020200000 + enum O_ASYNC = 0x2000; // octal 020000 + enum O_NOATIME = 0x40000; // octal 01000000 + enum O_PATH = 0x200000; // octal 010000000 + enum O_NDELAY = O_NONBLOCK; } else version (HPPA_Any) { @@ -177,6 +192,16 @@ version (CRuntime_Glibc) enum O_SYNC = 0x48000; // octal 01100000 enum O_DSYNC = 0x40000; // octal 01000000 enum O_RSYNC = 0x80000; // octal 02000000 + + enum O_DIRECTORY = 0x001000; // octal 000010000 + enum O_NOFOLLOW = 0x000080; // octal 000000200 + enum O_DIRECT = 0x004000; // octal 040000 + enum O_LARGEFILE = 0x000800; // octal 00004000 + enum O_TMPFILE = 0x801000; // octal 040010000 + enum O_ASYNC = 0x2000; // octal 020000 + enum O_NOATIME = 0x100000; // octal 004000000 + enum O_PATH = 0x400000; // octal 020000000 + enum O_NDELAY = O_NONBLOCK; } else version (MIPS_Any) { @@ -191,6 +216,19 @@ version (CRuntime_Glibc) enum O_CLOEXEC = 0x80000; enum O_RSYNC = O_SYNC; enum O_SYNC = 0x4010; + + enum O_DIRECTORY = 0x010000; + enum O_NOFOLLOW = 0x020000; + enum O_DIRECT = 0x8000; + version (MIPS_N64) + enum O_LARGEFILE = 0; + else + enum O_LARGEFILE = 0x2000; + enum O_TMPFILE = 0x410000; + enum O_ASYNC = 0x1000; + enum O_NOATIME = 0x40000; + enum O_PATH = 0x200000; + enum O_NDELAY = O_NONBLOCK; } else version (PPC_Any) { @@ -205,6 +243,19 @@ version (CRuntime_Glibc) enum O_SYNC = 0x101000; // octal 04010000 enum O_DSYNC = 0x1000; // octal 010000 enum O_RSYNC = O_SYNC; + + enum O_DIRECTORY = 0x004000; // octal 040000 + enum O_NOFOLLOW = 0x008000; // octal 0100000 + enum O_DIRECT = 0x020000; // octal 0400000 + version (D_LP64) + enum O_LARGEFILE = 0; + else + enum O_LARGEFILE = 0x10000; // octal 0200000 + enum O_TMPFILE = 0x404000; // octal 020040000 + enum O_ASYNC = 0x2000; // octal 020000 + enum O_NOATIME = 0x40000; // octal 01000000 + enum O_PATH = 0x200000; + enum O_NDELAY = O_NONBLOCK; } else version (ARM_Any) { @@ -219,6 +270,19 @@ version (CRuntime_Glibc) enum O_SYNC = 0x101000; // octal 04010000 enum O_DSYNC = 0x1000; // octal 010000 enum O_RSYNC = O_SYNC; + + enum O_DIRECTORY = 0x004000; // octal 040000 + enum O_NOFOLLOW = 0x008000; // octal 0100000 + enum O_DIRECT = 0x010000; // octal 0200000 + version (D_LP64) + enum O_LARGEFILE = 0; + else + enum O_LARGEFILE = 0x20000; // octal 0400000 + enum O_TMPFILE = 0x404000; // octal 020040000 + enum O_ASYNC = 0x2000; // octal 020000 + enum O_NOATIME = 0x40000; // octal 01000000 + enum O_PATH = 0x200000; // octal 010000000 + enum O_NDELAY = O_NONBLOCK; } else version (RISCV_Any) { @@ -233,6 +297,19 @@ version (CRuntime_Glibc) enum O_SYNC = 0x101000; // octal 04010000 enum O_DSYNC = 0x1000; // octal 010000 enum O_RSYNC = O_SYNC; + + enum O_DIRECTORY = 0x010000; + enum O_NOFOLLOW = 0x020000; + enum O_DIRECT = 0x004000; + version (D_LP64) + enum O_LARGEFILE = 0; + else + enum O_LARGEFILE = 0x8000; + enum O_TMPFILE = 0x410000; + enum O_ASYNC = 0x2000; + enum O_NOATIME = 0x40000; + enum O_PATH = 0x200000; + enum O_NDELAY = O_NONBLOCK; } else version (SPARC_Any) { @@ -247,6 +324,19 @@ version (CRuntime_Glibc) enum O_SYNC = 0x802000; enum O_DSYNC = 0x2000; enum O_RSYNC = O_SYNC; + + enum O_DIRECTORY = 0x10000; + enum O_NOFOLLOW = 0x20000; + enum O_DIRECT = 0x100000; + version (D_LP64) + enum O_LARGEFILE = 0; + else + enum O_LARGEFILE = 0x40000; + enum O_TMPFILE = 0x2010000; + enum O_ASYNC = 0x0040; + enum O_NOATIME = 0x200000; + enum O_PATH = 0x1000000; + enum O_NDELAY = (0x0004|O_NONBLOCK); } else version (IBMZ_Any) { @@ -261,11 +351,33 @@ version (CRuntime_Glibc) enum O_SYNC = 0x101000; // octal 04010000 enum O_DSYNC = 0x1000; // octal 010000 enum O_RSYNC = O_SYNC; + + enum O_DIRECTORY = 0x010000; // octal 0200000 + enum O_NOFOLLOW = 0x020000; // octal 0400000 + enum O_DIRECT = 0x004000; // octal 040000 + version (D_LP64) + enum O_LARGEFILE = 0; + else + enum O_LARGEFILE = 0x08000; // octal 0100000 + enum O_TMPFILE = 0x410000; // octal 020200000 + enum O_ASYNC = 0x2000; // octal 020000 + enum O_NOATIME = 0x40000; // octal 01000000 + enum O_PATH = 0x200000; // octal 010000000 + enum O_NDELAY = O_NONBLOCK; } else static assert(0, "unimplemented"); - enum O_ACCMODE = 0x3; + version (CRuntime_Musl) + { + enum O_SEARCH = O_PATH; + enum O_EXEC = O_PATH; + enum O_ACCMODE = (3|O_SEARCH); + } + else + { + enum O_ACCMODE = 0x3; + } enum O_RDONLY = 0x0; enum O_WRONLY = 0x1; enum O_RDWR = 0x2; @@ -279,22 +391,11 @@ version (CRuntime_Glibc) pid_t l_pid; } - static if ( __USE_FILE_OFFSET64 ) - { - int creat64(const scope char*, mode_t); - alias creat64 creat; - - int open64(const scope char*, int, ...); - alias open64 open; - } - else - { - int creat(const scope char*, mode_t); - int open(const scope char*, int, ...); - } - enum AT_SYMLINK_NOFOLLOW = 0x100; enum AT_FDCWD = -100; + enum AT_REMOVEDIR = 0x200; + enum AT_SYMLINK_FOLLOW = 0x400; + enum AT_EACCESS = 0x200; } else version (Darwin) { @@ -339,9 +440,6 @@ else version (Darwin) short l_type; short l_whence; } - - int creat(const scope char*, mode_t); - int open(const scope char*, int, ...); } else version (FreeBSD) { @@ -401,9 +499,6 @@ else version (FreeBSD) short l_whence; } - int creat(const scope char*, mode_t); - int open(const scope char*, int, ...); - enum AT_SYMLINK_NOFOLLOW = 0x200; enum AT_FDCWD = -100; } @@ -466,9 +561,6 @@ else version (OpenBSD) short l_whence; } - int creat(const scope char*, mode_t); - int open(const scope char*, int, ...); - enum AT_FDCWD = -100; enum AT_EACCESS = 0x01; @@ -524,10 +616,6 @@ else version (NetBSD) short l_type; short l_whence; } - - - int creat(const scope char*, mode_t); - int open(const scope char*, int, ...); } else version (DragonFlyBSD) { @@ -612,11 +700,6 @@ else version (DragonFlyBSD) } alias oflock = flock; - - int creat(const scope char*, mode_t); - int open(const scope char*, int, ...); - //int fcntl(int, int, ...); /*defined below*/ - //int flock(int, int); } else version (Solaris) { @@ -700,7 +783,60 @@ else version (Solaris) c_long[4] l_pad; } } +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +int creat(const scope char*, mode_t); +int fcntl(int, int, ...); +int open(const scope char*, int, ...); +*/ +version (CRuntime_Glibc) +{ + static if ( __USE_FILE_OFFSET64 ) + { + int creat64(const scope char*, mode_t); + alias creat64 creat; + int open64(const scope char*, int, ...); + alias open64 open; + } + else + { + int creat(const scope char*, mode_t); + int open(const scope char*, int, ...); + } +} +else version (Darwin) +{ + int creat(const scope char*, mode_t); + int open(const scope char*, int, ...); +} +else version (FreeBSD) +{ + int creat(const scope char*, mode_t); + int open(const scope char*, int, ...); +} +else version (OpenBSD) +{ + int creat(const scope char*, mode_t); + int open(const scope char*, int, ...); +} +else version (NetBSD) +{ + int creat(const scope char*, mode_t); + int open(const scope char*, int, ...); +} +else version (DragonFlyBSD) +{ + int creat(const scope char*, mode_t); + int open(const scope char*, int, ...); +} +else version (Solaris) +{ version (D_LP64) { int creat(const scope char*, mode_t); @@ -731,323 +867,15 @@ else version (Solaris) } else version (CRuntime_Bionic) { - // All these except for the two functions open and creat really come from - // the linux kernel and can probably be merged. - enum F_DUPFD = 0; - enum F_GETFD = 1; - enum F_SETFD = 2; - enum F_GETFL = 3; - enum F_SETFL = 4; - enum F_GETLK = 5; - enum F_SETLK = 6; - enum F_SETLKW = 7; - enum F_SETOWN = 8; - enum F_GETOWN = 9; - - enum FD_CLOEXEC = 1; - - enum F_RDLCK = 0; - enum F_WRLCK = 1; - enum F_UNLCK = 2; - - enum O_CREAT = 0x40; // octal 0100 - enum O_EXCL = 0x80; // octal 0200 - enum O_NOCTTY = 0x100; // octal 0400 - enum O_TRUNC = 0x200; // octal 01000 - - enum O_APPEND = 0x400; // octal 02000 - enum O_NONBLOCK = 0x800; // octal 04000 - - version (D_LP64) - { - enum O_SYNC = 0x101000; // octal 04010000 - } - else - { - enum O_SYNC = 0x1000; // octal 010000 - } - - enum O_ACCMODE = 0x3; - enum O_RDONLY = 0x0; - enum O_WRONLY = 0x1; - enum O_RDWR = 0x2; - - struct flock - { - short l_type; - short l_whence; - off_t l_start; - off_t l_len; - pid_t l_pid; - } - int creat(const scope char*, mode_t); int open(const scope char*, int, ...); - - enum AT_FDCWD = -100; } else version (CRuntime_Musl) { - version (X86_64) - { - enum - { - O_DIRECTORY = 0x010000, // octal 0200000 - O_NOFOLLOW = 0x020000, // octal 0400000 - O_DIRECT = 0x004000, // octal 040000 - O_LARGEFILE = 0, - O_TMPFILE = 0x410000, // octal 020200000 - - F_GETLK = 5, - F_SETLK = 6, - F_SETLKW = 7, - } - } - // Note: Definitions for i386 are in arch/generic/bits/fcntl.h - else version (X86) - { - enum - { - O_DIRECTORY = 0x010000, // octal 0200000 - O_NOFOLLOW = 0x020000, // octal 0400000 - O_DIRECT = 0x004000, // octal 040000 - O_LARGEFILE = 0x008000, // octal 0100000 - O_TMPFILE = 0x410000, // octal 020200000 - - F_GETLK = 12, - F_SETLK = 13, - F_SETLKW = 14, - } - } - else version (ARM) - { - enum - { - O_DIRECTORY = 0x004000, // octal 040000 - O_NOFOLLOW = 0x008000, // octal 0100000 - O_DIRECT = 0x010000, // octal 0200000 - O_LARGEFILE = 0x020000, // octal 0400000 - O_TMPFILE = 0x404000, // octal 020040000 - - F_GETLK = 12, - F_SETLK = 13, - F_SETLKW = 14, - } - } - else version (AArch64) - { - enum - { - O_DIRECTORY = 0x004000, // octal 040000 - O_NOFOLLOW = 0x008000, // octal 0100000 - O_DIRECT = 0x010000, // octal 0200000 - O_LARGEFILE = 0x020000, // octal 0400000 - O_TMPFILE = 0x404000, // octal 020040000 - - F_GETLK = 5, - F_SETLK = 6, - F_SETLKW = 7, - } - } - else version (SystemZ) - { - enum - { - O_DIRECTORY = 0x010000, // octal 0200000 - O_NOFOLLOW = 0x020000, // octal 0400000 - O_DIRECT = 0x004000, // octal 040000 - O_LARGEFILE = 0x008000, // octal 0100000 - O_TMPFILE = 0x410000, // octal 020200000 - - F_GETLK = 5, - F_SETLK = 6, - F_SETLKW = 7, - } - } - else version (PPC64) - { - enum - { - O_DIRECTORY = 0x004000, // octal 040000 - O_NOFOLLOW = 0x008000, // octal 0100000 - O_DIRECT = 0x020000, // octal 0400000 - O_LARGEFILE = 0x010000, // octal 0200000 - O_TMPFILE = 0x410000, // octal 020200000 - - F_GETLK = 5, - F_SETLK = 6, - F_SETLKW = 7, - } - } - else - static assert(0, "Platform not supported"); - - enum - { - O_CREAT = 0x40, // octal 0100 - O_EXCL = 0x80, // octal 0200 - O_NOCTTY = 0x100, // octal 0400 - O_TRUNC = 0x200, // octal 01000 - - O_APPEND = 0x400, // octal 02000 - O_NONBLOCK = 0x800, // octal 04000 - O_DSYNC = 0x1000, // octal 010000 - O_SYNC = 0x101000, // octal 04010000 - O_RSYNC = O_SYNC, - O_CLOEXEC = 0x80000, - - O_ASYNC = 0x2000, - O_NOATIME = 0x40000, - O_PATH = 0x200000, - O_NDELAY = O_NONBLOCK, - O_SEARCH = O_PATH, - O_EXEC = O_PATH, - - O_ACCMODE = (3|O_SEARCH), - O_RDONLY = 0, - O_WRONLY = 1, - O_RDWR = 2, - } - enum - { - F_DUPFD = 0, - F_GETFD = 1, - F_SETFD = 2, - F_GETFL = 3, - F_SETFL = 4, - // F_GETLK, F_SETLK, F_SETLKW are arch-specific - F_SETOWN = 8, - F_GETOWN = 9, - } - enum - { - F_RDLCK = 0, - F_WRLCK = 1, - F_UNLCK = 2, - } - struct flock - { - short l_type; - short l_whence; - off_t l_start; - off_t l_len; - pid_t l_pid; - } - enum FD_CLOEXEC = 1; int open(const scope char*, int, ...); - - enum AT_FDCWD = -100; - enum AT_SYMLINK_NOFOLLOW = 0x100; - enum AT_REMOVEDIR = 0x200; - enum AT_SYMLINK_FOLLOW = 0x400; - enum AT_EACCESS = 0x200; } else version (CRuntime_UClibc) { - enum F_DUPFD = 0; - enum F_GETFD = 1; - enum F_SETFD = 2; - enum F_GETFL = 3; - enum F_SETFL = 4; - - version (X86_64) - { - enum F_GETLK = 5; - enum F_SETLK = 6; - enum F_SETLKW = 7; - } - else static if (__USE_FILE_OFFSET64) - { - enum F_GETLK = 5; - enum F_SETLK = 6; - enum F_SETLKW = 7; - } - else - { - enum F_GETLK = 12; - enum F_SETLK = 13; - enum F_SETLKW = 14; - } - - enum F_GETOWN = 9; - enum F_SETOWN = 8; - - enum FD_CLOEXEC = 1; - - enum F_RDLCK = 0; - enum F_UNLCK = 2; - enum F_WRLCK = 1; - - version (X86_Any) - { - enum O_CREAT = 0x40; // octal 0100 - enum O_EXCL = 0x80; // octal 0200 - enum O_NOCTTY = 0x100; // octal 0400 - enum O_TRUNC = 0x200; // octal 01000 - - enum O_APPEND = 0x400; // octal 02000 - enum O_NONBLOCK = 0x800; // octal 04000 - enum O_CLOEXEC = 0x80000; // octal 02000000 - enum O_SYNC = 0x1000; // octal 010000 - enum O_NDELAY = O_NONBLOCK; - enum O_FSYNC = O_SYNC; - enum O_ASYNC = 0x2000; // octal 020000 - } - else version (MIPS_Any) - { - enum O_CREAT = 0x0100; - enum O_EXCL = 0x0400; - enum O_NOCTTY = 0x0800; - enum O_TRUNC = 0x0200; - - enum O_APPEND = 0x0008; - enum O_SYNC = 0x0010; - enum O_NONBLOCK = 0x0080; - enum O_CLOEXEC = 0x80000; // octal 02000000 - enum O_NDELAY = O_NONBLOCK; - enum O_FSYNC = O_SYNC; - enum O_ASYNC = 0x1000; - } - else version (ARM_Any) - { - enum O_CREAT = 0x40; // octal 0100 - enum O_EXCL = 0x80; // octal 0200 - enum O_NOCTTY = 0x100; // octal 0400 - enum O_TRUNC = 0x200; // octal 01000 - - enum O_APPEND = 0x400; // octal 02000 - enum O_NONBLOCK = 0x800; // octal 04000 - enum O_CLOEXEC = 0x80000; // octal 02000000 - enum O_SYNC = 0x1000; // octal 010000 - enum O_NDELAY = O_NONBLOCK; - enum O_FSYNC = O_SYNC; - enum O_ASYNC = 0x2000; // octal 020000 - } - else - static assert(0, "unimplemented"); - - enum O_ACCMODE = 0x3; - enum O_RDONLY = 0x0; - enum O_WRONLY = 0x1; - enum O_RDWR = 0x2; - - struct flock - { - short l_type; - short l_whence; - static if (__USE_FILE_OFFSET64) - { - off64_t l_start; - off64_t l_len; - } - else - { - off_t l_start; - off_t l_len; - } - pid_t l_pid; - } - static if ( __USE_FILE_OFFSET64 ) { int creat64(const scope char*, mode_t); @@ -1061,9 +889,6 @@ else version (CRuntime_UClibc) int creat(const scope char*, mode_t); int open(const scope char*, int, ...); } - - enum AT_SYMLINK_NOFOLLOW = 0x100; - enum AT_FDCWD = -100; } else { diff --git a/libphobos/libdruntime/core/sys/posix/poll.d b/libphobos/libdruntime/core/sys/posix/poll.d index fdc41764a78..7c2d5705e4f 100644 --- a/libphobos/libdruntime/core/sys/posix/poll.d +++ b/libphobos/libdruntime/core/sys/posix/poll.d @@ -44,17 +44,6 @@ struct pollfd nfds_t -POLLIN -POLLRDNORM -POLLRDBAND -POLLPRI -POLLOUT -POLLWRNORM -POLLWRBAND -POLLERR -POLLHUP -POLLNVAL - int poll(pollfd[], nfds_t, int); */ @@ -69,6 +58,145 @@ version (CRuntime_Glibc) alias c_ulong nfds_t; + int poll(pollfd*, nfds_t, int); +} +else version (Darwin) +{ + struct pollfd + { + int fd; + short events; + short revents; + } + + alias uint nfds_t; + + int poll(pollfd*, nfds_t, int); +} +else version (FreeBSD) +{ + alias uint nfds_t; + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll(pollfd*, nfds_t, int); +} +else version (NetBSD) +{ + alias uint nfds_t; + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll(pollfd*, nfds_t, int); +} +else version (OpenBSD) +{ + alias uint nfds_t; + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll(pollfd*, nfds_t, int); +} +else version (DragonFlyBSD) +{ + alias uint nfds_t; + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll(pollfd*, nfds_t, int); +} +else version (Solaris) +{ + alias c_ulong nfds_t; + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll(pollfd*, nfds_t, int); +} +else version (CRuntime_Bionic) +{ + struct pollfd + { + int fd; + short events; + short revents; + } + + alias uint nfds_t; + + int poll(pollfd*, nfds_t, c_long); +} +else version (CRuntime_Musl) +{ + struct pollfd + { + int fd; + short events; + short revents; + } + + alias uint nfds_t; + + int poll(pollfd*, nfds_t, c_long); +} +else version (CRuntime_UClibc) +{ + struct pollfd + { + int fd; + short events; + short revents; + } + + alias c_ulong nfds_t; + + int poll(pollfd*, nfds_t, int); +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +POLLIN +POLLRDNORM +POLLRDBAND +POLLPRI +POLLOUT +POLLWRNORM +POLLWRBAND +POLLERR +POLLHUP +POLLNVAL +*/ + +version (linux) +{ enum { POLLIN = 0x001, @@ -82,20 +210,9 @@ version (CRuntime_Glibc) POLLHUP = 0x010, POLLNVAL = 0x020, } - - int poll(pollfd*, nfds_t, int); } else version (Darwin) { - struct pollfd - { - int fd; - short events; - short revents; - } - - alias uint nfds_t; - enum { POLLIN = 0x0001, @@ -116,20 +233,9 @@ else version (Darwin) POLLSTANDARD = (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| POLLWRBAND|POLLERR|POLLHUP|POLLNVAL) } - - int poll(pollfd*, nfds_t, int); } else version (FreeBSD) { - alias uint nfds_t; - - struct pollfd - { - int fd; - short events; - short revents; - } - enum { POLLIN = 0x0001, @@ -150,20 +256,9 @@ else version (FreeBSD) POLLSTANDARD = (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| POLLWRBAND|POLLERR|POLLHUP|POLLNVAL) } - - int poll(pollfd*, nfds_t, int); } else version (NetBSD) { - alias uint nfds_t; - - struct pollfd - { - int fd; - short events; - short revents; - } - enum { POLLIN = 0x0001, @@ -184,20 +279,9 @@ else version (NetBSD) POLLSTANDARD = (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| POLLWRBAND|POLLERR|POLLHUP|POLLNVAL) } - - int poll(pollfd*, nfds_t, int); } else version (OpenBSD) { - alias uint nfds_t; - - struct pollfd - { - int fd; - short events; - short revents; - } - enum { POLLIN = 0x0001, @@ -215,20 +299,9 @@ else version (OpenBSD) POLLSTANDARD = (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| POLLWRBAND|POLLERR|POLLHUP|POLLNVAL) } - - int poll(pollfd*, nfds_t, int); } else version (DragonFlyBSD) { - alias uint nfds_t; - - struct pollfd - { - int fd; - short events; - short revents; - } - enum { POLLIN = 0x0001, @@ -249,20 +322,9 @@ else version (DragonFlyBSD) POLLSTANDARD = (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| POLLWRBAND|POLLERR|POLLHUP|POLLNVAL) } - - int poll(pollfd*, nfds_t, int); } else version (Solaris) { - alias c_ulong nfds_t; - - struct pollfd - { - int fd; - short events; - short revents; - } - enum { POLLIN = 0x0001, @@ -276,90 +338,8 @@ else version (Solaris) POLLHUP = 0x0010, POLLNVAL = 0x0020, } - - int poll(pollfd*, nfds_t, int); } -else version (CRuntime_Bionic) +else { - struct pollfd - { - int fd; - short events; - short revents; - } - - alias uint nfds_t; - - enum - { - POLLIN = 0x001, - POLLRDNORM = 0x040, - POLLRDBAND = 0x080, - POLLPRI = 0x002, - POLLOUT = 0x004, - POLLWRNORM = 0x100, - POLLWRBAND = 0x200, - POLLERR = 0x008, - POLLHUP = 0x010, - POLLNVAL = 0x020, - } - - int poll(pollfd*, nfds_t, c_long); -} -else version (CRuntime_Musl) -{ - struct pollfd - { - int fd; - short events; - short revents; - } - - alias uint nfds_t; - - enum - { - POLLIN = 0x001, - POLLPRI = 0x002, - POLLOUT = 0x004, - POLLERR = 0x008, - POLLHUP = 0x010, - POLLNVAL = 0x020, - POLLRDNORM = 0x040, - POLLRDBAND = 0x080, - POLLWRNORM = 0x100, - POLLWRBAND = 0x200, - } - - int poll(pollfd*, nfds_t, c_long); -} -else version (CRuntime_UClibc) -{ - struct pollfd - { - int fd; - short events; - short revents; - } - - alias c_ulong nfds_t; - - enum - { - POLLIN = 0x001, - POLLRDNORM = 0x040, - POLLRDBAND = 0x080, - POLLPRI = 0x002, - POLLOUT = 0x004, - POLLWRNORM = 0x100, - POLLWRBAND = 0x200, - POLLMSG = 0x400, - POLLREMOVE = 0x1000, - POLLRDHUP = 0x2000, - POLLERR = 0x008, - POLLHUP = 0x010, - POLLNVAL = 0x020, - } - - int poll(pollfd*, nfds_t, int); + static assert(false, "Unsupported platform"); } diff --git a/libphobos/libdruntime/core/sys/posix/sched.d b/libphobos/libdruntime/core/sys/posix/sched.d index f9d286217fb..35463d4fb6c 100644 --- a/libphobos/libdruntime/core/sys/posix/sched.d +++ b/libphobos/libdruntime/core/sys/posix/sched.d @@ -58,27 +58,33 @@ int sched_setparam(pid_t, const scope sched_param*); int sched_setscheduler(pid_t, int, const scope sched_param*); */ -version (CRuntime_Glibc) +version (linux) { - struct sched_param + version (CRuntime_Musl) { - int sched_priority; + struct sched_param + { + int sched_priority; + int __reserved1; + timespec[2] __reserved2; + int __reserved3; + } + } + else + { + struct sched_param + { + int sched_priority; + } } enum SCHED_OTHER = 0; enum SCHED_FIFO = 1; enum SCHED_RR = 2; //SCHED_SPORADIC (SS|TSP) -} -else version (CRuntime_Musl) -{ - struct sched_param { - int sched_priority; - int sched_ss_low_priority; - timespec sched_ss_repl_period; - timespec sched_ss_init_budget; - int sched_ss_max_repl; - } + enum SCHED_BATCH = 3; + enum SCHED_IDLE = 5; + enum SCHED_RESET_ON_FORK = 0x40000000; } else version (Darwin) { @@ -87,8 +93,6 @@ else version (Darwin) enum SCHED_RR = 2; //SCHED_SPORADIC (SS|TSP) - private enum __SCHED_PARAM_SIZE__ = 4; - struct sched_param { int sched_priority; @@ -156,33 +160,6 @@ else version (Solaris) enum SCHED_FX = 6; enum _SCHED_NEXT = 7; } -else version (CRuntime_Bionic) -{ - struct sched_param - { - int sched_priority; - } - - enum SCHED_NORMAL = 0; - enum SCHED_OTHER = 0; - enum SCHED_FIFO = 1; - enum SCHED_RR = 2; -} -else version (CRuntime_UClibc) -{ - struct sched_param - { - int sched_priority; - } - - enum SCHED_OTHER = 0; - enum SCHED_FIFO = 1; - enum SCHED_RR = 2; - enum SCHED_BATCH = 3; - enum SCHED_IDLE = 5; - - enum SCHED_RESET_ON_FORK = 0x40000000; -} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/signal.d b/libphobos/libdruntime/core/sys/posix/signal.d index 32e51561562..68aee980ef3 100644 --- a/libphobos/libdruntime/core/sys/posix/signal.d +++ b/libphobos/libdruntime/core/sys/posix/signal.d @@ -151,13 +151,15 @@ version (Solaris) return sig; } } -else version (FreeBSD) { +else version (FreeBSD) +{ // Note: it appears that FreeBSD (prior to 7) and OSX do not support realtime signals // https://github.com/freebsd/freebsd/blob/e79c62ff68fc74d88cb6f479859f6fae9baa5101/sys/sys/signal.h#L117 enum SIGRTMIN = 65; enum SIGRTMAX = 126; } -else version (DragonFlyBSD) { +else version (DragonFlyBSD) +{ enum SIGRTMIN = 35; enum SIGRTMAX = 126; } @@ -540,9 +542,54 @@ else static assert(false, "Unsupported platform"); } -version (CRuntime_Glibc) +version (linux) { - version (SystemZ) + version (CRuntime_Musl) + { + struct sigaction_t + { + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + sigset_t sa_mask; + int sa_flags; + void function() sa_restorer; + } + } + else version (CRuntime_Bionic) + { + version (D_LP64) + { + struct sigaction_t + { + int sa_flags; + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + sigset_t sa_mask; + void function() sa_restorer; + } + } + else + { + struct sigaction_t + { + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + sigset_t sa_mask; + int sa_flags; + void function() sa_restorer; + } + } + } + else version (SystemZ) { struct sigaction_t { @@ -558,15 +605,22 @@ version (CRuntime_Glibc) { sigfn_t sa_handler; } - int __glibc_reserved0; - int sa_flags; + version (CRuntime_Glibc) + { + int __glibc_reserved0; + int sa_flags; + } + else + { + c_ulong sa_flags; + } void function() sa_restorer; sigset_t sa_mask; } } - else + else version (HPPA_Any) { struct sigaction_t { @@ -582,33 +636,100 @@ version (CRuntime_Glibc) { sigfn_t sa_handler; } + version (CRuntime_Glibc) + { + version (D_LP64) + int __glibc_reserved0; + int sa_flags; + } + else + { + c_ulong sa_flags; + } sigset_t sa_mask; - int sa_flags; + } + } + else version (MIPS_Any) + { + struct sigaction_t + { + int sa_flags; + static if ( true /* __USE_POSIX199309 */ ) + { + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + } + else + { + sigfn_t sa_handler; + } + sigset_t sa_mask; void function() sa_restorer; + + version (CRuntime_Glibc) + { + static if ((void*).sizeof < 8) + int[1] sa_resv; + } } } -} -else version (CRuntime_Musl) -{ - struct sigaction_t + else version (SPARC_Any) { - static if ( true /* __USE_POSIX199309 */ ) + struct sigaction_t { - union + static if ( true /* __USE_POSIX199309 */ ) + { + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + } + else { sigfn_t sa_handler; - sigactfn_t sa_sigaction; + } + version (CRuntime_Glibc) + { + sigset_t sa_mask; + version (D_LP64) + int __glibc_reserved0; + int sa_flags; + void function() sa_restorer; + } + else + { + c_ulong sa_flags; + void function() sa_restorer; + sigset_t sa_mask; } } - else + } + else + { + struct sigaction_t { - sigfn_t sa_handler; - } - sigset_t sa_mask; - int sa_flags; + static if ( true /* __USE_POSIX199309 */ ) + { + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + } + else + { + sigfn_t sa_handler; + } + sigset_t sa_mask; + int sa_flags; - void function() sa_restorer; + void function() sa_restorer; + } } } else version (FreeBSD) @@ -683,91 +804,6 @@ else version (Solaris) int[2] sa_resv; } } -else version (CRuntime_UClibc) -{ - version (ARM) version = sigaction_common; - else version (X86_64) version = sigaction_common; - - version (sigaction_common) - { - struct sigaction_t - { - static if ( true /* __USE_POSIX199309 */ ) - { - union - { - sigfn_t sa_handler; - sigactfn_t sa_sigaction; - } - } - else - { - sigfn_t sa_handler; - } - c_ulong sa_flags; - void function() sa_restorer; - sigset_t sa_mask; - } - } - else version (MIPS32) - { - struct sigaction_t - { - uint sa_flags; - static if ( true /* __USE_POSIX199309 */ ) - { - union - { - sigfn_t sa_handler; - sigactfn_t sa_sigaction; - } - } - else - { - sigfn_t sa_handler; - } - sigset_t sa_mask; - void function() sa_restorer; - } - } - else - { - static assert(false, "Architecture not supported."); - } -} -else version (CRuntime_Bionic) -{ - version (D_LP64) - { - struct sigaction_t - { - int sa_flags; - union - { - sigfn_t sa_handler; - sigactfn_t sa_sigaction; - } - - sigset_t sa_mask; - void function() sa_restorer; - } - } - else - { - struct sigaction_t - { - union - { - sigfn_t sa_handler; - sigactfn_t sa_sigaction; - } - - sigset_t sa_mask; - int sa_flags; - void function() sa_restorer; - } - } -} else version (Darwin) { struct sigaction_t @@ -839,26 +875,14 @@ SI_QUEUE SI_TIMER SI_ASYNCIO SI_MESGQ - -int kill(pid_t, int); -int sigaction(int, const scope sigaction_t*, sigaction_t*); -int sigaddset(sigset_t*, int); -int sigdelset(sigset_t*, int); -int sigemptyset(sigset_t*); -int sigfillset(sigset_t*); -int sigismember(const scope sigset_t*, int); -int sigpending(sigset_t*); -int sigprocmask(int, const scope sigset_t*, sigset_t*); -int sigsuspend(const scope sigset_t*); -int sigwait(const scope sigset_t*, int*); */ nothrow @nogc { -version (CRuntime_Glibc) +version (linux) { - enum SIG_HOLD = cast(sigfn_t2) 1; + enum SIG_HOLD = cast(sigfn_t2) 2; private enum _SIGSET_NWORDS = 1024 / (8 * c_ulong.sizeof); @@ -867,20 +891,26 @@ version (CRuntime_Glibc) c_ulong[_SIGSET_NWORDS] __val; } - // pid_t (defined in core.sys.types) - - //SIGABRT (defined in core.stdc.signal) - //SIGFPE (defined in core.stdc.signal) - //SIGILL (defined in core.stdc.signal) - //SIGINT (defined in core.stdc.signal) - //SIGSEGV (defined in core.stdc.signal) - //SIGTERM (defined in core.stdc.signal) - enum SA_NOCLDSTOP = 1; // (CX|XSI) - enum SIG_BLOCK = 0; - enum SIG_UNBLOCK = 1; - enum SIG_SETMASK = 2; + version (MIPS_Any) + { + enum SIG_BLOCK = 1; + enum SIG_UNBLOCK = 2; + enum SIG_SETMASK = 3; + } + else version (SPARC_Any) + { + enum SIG_BLOCK = 1; + enum SIG_UNBLOCK = 2; + enum SIG_SETMASK = 4; + } + else + { + enum SIG_BLOCK = 0; + enum SIG_UNBLOCK = 1; + enum SIG_SETMASK = 2; + } private enum __SI_MAX_SIZE = 128; @@ -895,10 +925,17 @@ version (CRuntime_Glibc) struct siginfo_t { - int si_signo; // Signal number - int si_errno; // If non-zero, an errno value associated with - // this signal, as defined in <errno.h> - int si_code; // Signal code + int si_signo; + version (MIPS_Any) // __SI_SWAP_ERRNO_CODE + { + int si_code; + int si_errno; + } + else + { + int si_errno; + int si_code; + } union _sifields_t { @@ -907,32 +944,31 @@ version (CRuntime_Glibc) // kill() struct _kill_t { - pid_t si_pid; // Sending process ID - uid_t si_uid; // Real user ID of sending process + pid_t si_pid; + uid_t si_uid; } _kill_t _kill; - // POSIX.1b timers. struct _timer_t { - int si_tid; // Timer ID - int si_overrun; // Overrun count - sigval si_sigval; // Signal value + int si_tid; + int si_overrun; + sigval si_sigval; } _timer_t _timer; // POSIX.1b signals struct _rt_t { - pid_t si_pid; // Sending process ID - uid_t si_uid; // Real user ID of sending process - sigval si_sigval; // Signal value + pid_t si_pid; + uid_t si_uid; + sigval si_sigval; } _rt_t _rt; // SIGCHLD struct _sigchild_t { - pid_t si_pid; // Which child - uid_t si_uid; // Real user ID of sending process - int si_status; // Exit value or signal + pid_t si_pid; + uid_t si_uid; + int si_status; clock_t si_utime; clock_t si_stime; } _sigchild_t _sigchld; @@ -940,13 +976,13 @@ version (CRuntime_Glibc) // SIGILL, SIGFPE, SIGSEGV, SIGBUS struct _sigfault_t { - void* si_addr; // Faulting insn/memory ref + void* si_addr; } _sigfault_t _sigfault; // SIGPOLL struct _sigpoll_t { - c_long si_band; // Band event for SIGPOLL + c_long si_band; int si_fd; } _sigpoll_t _sigpoll; } _sifields_t _sifields; @@ -972,32 +1008,12 @@ version (CRuntime_Glibc) SI_USER, SI_KERNEL = 0x80 } - - int kill(pid_t, int); - int sigaction(int, const scope sigaction_t*, sigaction_t*); - int sigaddset(sigset_t*, int); - int sigdelset(sigset_t*, int); - int sigemptyset(sigset_t*); - int sigfillset(sigset_t*); - int sigismember(const scope sigset_t*, int); - int sigpending(sigset_t*); - int sigprocmask(int, const scope sigset_t*, sigset_t*); - int sigsuspend(const scope sigset_t*); - int sigwait(const scope sigset_t*, int*); } else version (Darwin) { enum SIG_HOLD = cast(sigfn_t2) 5; alias uint sigset_t; - // pid_t (defined in core.sys.types) - - //SIGABRT (defined in core.stdc.signal) - //SIGFPE (defined in core.stdc.signal) - //SIGILL (defined in core.stdc.signal) - //SIGINT (defined in core.stdc.signal) - //SIGSEGV (defined in core.stdc.signal) - //SIGTERM (defined in core.stdc.signal) enum SA_NOCLDSTOP = 8; // (CX|XSI) @@ -1024,18 +1040,6 @@ else version (Darwin) enum SI_TIMER = 0x10003; enum SI_ASYNCIO = 0x10004; enum SI_MESGQ = 0x10005; - - int kill(pid_t, int); - int sigaction(int, const scope sigaction_t*, sigaction_t*); - int sigaddset(sigset_t*, int); - int sigdelset(sigset_t*, int); - int sigemptyset(sigset_t*); - int sigfillset(sigset_t*); - int sigismember(const scope sigset_t*, int); - int sigpending(sigset_t*); - int sigprocmask(int, const scope sigset_t*, sigset_t*); - int sigsuspend(const scope sigset_t*); - int sigwait(const scope sigset_t*, int*); } else version (FreeBSD) { @@ -1102,18 +1106,6 @@ else version (FreeBSD) enum SI_TIMER = 0x10003; enum SI_ASYNCIO = 0x10004; enum SI_MESGQ = 0x10005; - - int kill(pid_t, int); - int sigaction(int, const scope sigaction_t*, sigaction_t*); - int sigaddset(sigset_t*, int); - int sigdelset(sigset_t*, int); - int sigemptyset(sigset_t *); - int sigfillset(sigset_t *); - int sigismember(const scope sigset_t*, int); - int sigpending(sigset_t *); - int sigprocmask(int, const scope sigset_t*, sigset_t*); - int sigsuspend(const scope sigset_t*); - int sigwait(const scope sigset_t*, int*); } else version (NetBSD) { @@ -1188,28 +1180,6 @@ else version (NetBSD) enum SI_TIMER = -2; enum SI_ASYNCIO = -3; enum SI_MESGQ = -4; - - int kill(pid_t, int); - int __sigaction14(int, const scope sigaction_t*, sigaction_t*); - int __sigaddset14(sigset_t*, int); - int __sigdelset14(sigset_t*, int); - int __sigemptyset14(sigset_t *); - int __sigfillset14(sigset_t *); - int __sigismember14(const scope sigset_t*, int); - int __sigpending14(sigset_t *); - int __sigprocmask14(int, const scope sigset_t*, sigset_t*); - int __sigsuspend14(const scope sigset_t*); - int sigwait(const scope sigset_t*, int*); - - alias __sigaction14 sigaction; - alias __sigaddset14 sigaddset; - alias __sigdelset14 sigdelset; - alias __sigemptyset14 sigemptyset; - alias __sigfillset14 sigfillset; - alias __sigismember14 sigismember; - alias __sigpending14 sigpending; - alias __sigprocmask14 sigprocmask; - alias __sigsuspend14 sigsuspend; } else version (OpenBSD) { @@ -1274,18 +1244,6 @@ else version (OpenBSD) enum SI_LWP = -1; enum SI_QUEUE = -2; enum SI_TIMER = -3; - - int kill(pid_t, int); - int sigaction(int, const scope sigaction_t*, sigaction_t*); - int sigaddset(sigset_t*, int); - int sigdelset(sigset_t*, int); - int sigemptyset(sigset_t *); - int sigfillset(sigset_t *); - int sigismember(const scope sigset_t*, int); - int sigpending(sigset_t *); - int sigprocmask(int, const scope sigset_t*, sigset_t*); - int sigsuspend(const scope sigset_t*); - int sigwait(const scope sigset_t*, int*); } else version (DragonFlyBSD) { @@ -1323,18 +1281,6 @@ else version (DragonFlyBSD) enum SI_TIMER = -2; enum SI_ASYNCIO = -3; enum SI_MESGQ = -4; - - int kill(pid_t, int); - int sigaction(int, const scope sigaction_t*, sigaction_t*); - int sigaddset(sigset_t*, int); - int sigdelset(sigset_t*, int); - int sigemptyset(sigset_t *); - int sigfillset(sigset_t *); - int sigismember(const scope sigset_t*, int); - int sigpending(sigset_t *); - int sigprocmask(int, const scope sigset_t*, sigset_t*); - int sigsuspend(const scope sigset_t*); - int sigwait(const scope sigset_t*, int*); } else version (Solaris) { @@ -1446,9 +1392,122 @@ else version (Solaris) enum SI_TIMER = -3; enum SI_ASYNCIO = -4; enum SI_MESGQ = -5; +} +else +{ + static assert(false, "Unsupported platform"); +} - enum SIGIO = SIGPOLL; +/* +int kill(pid_t, int); +int sigaction(int, const scope sigaction_t*, sigaction_t*); +int sigaddset(sigset_t*, int); +int sigdelset(sigset_t*, int); +int sigemptyset(sigset_t*); +int sigfillset(sigset_t*); +int sigismember(const scope sigset_t*, int); +int sigpending(sigset_t*); +int sigprocmask(int, const scope sigset_t*, sigset_t*); +int sigsuspend(const scope sigset_t*); +int sigwait(const scope sigset_t*, int*); +*/ + +version (CRuntime_Glibc) +{ + int kill(pid_t, int); + int sigaction(int, const scope sigaction_t*, sigaction_t*); + int sigaddset(sigset_t*, int); + int sigdelset(sigset_t*, int); + int sigemptyset(sigset_t*); + int sigfillset(sigset_t*); + int sigismember(const scope sigset_t*, int); + int sigpending(sigset_t*); + int sigprocmask(int, const scope sigset_t*, sigset_t*); + int sigsuspend(const scope sigset_t*); + int sigwait(const scope sigset_t*, int*); +} +else version (Darwin) +{ + int kill(pid_t, int); + int sigaction(int, const scope sigaction_t*, sigaction_t*); + int sigaddset(sigset_t*, int); + int sigdelset(sigset_t*, int); + int sigemptyset(sigset_t*); + int sigfillset(sigset_t*); + int sigismember(const scope sigset_t*, int); + int sigpending(sigset_t*); + int sigprocmask(int, const scope sigset_t*, sigset_t*); + int sigsuspend(const scope sigset_t*); + int sigwait(const scope sigset_t*, int*); +} +else version (FreeBSD) +{ + int kill(pid_t, int); + int sigaction(int, const scope sigaction_t*, sigaction_t*); + int sigaddset(sigset_t*, int); + int sigdelset(sigset_t*, int); + int sigemptyset(sigset_t *); + int sigfillset(sigset_t *); + int sigismember(const scope sigset_t*, int); + int sigpending(sigset_t *); + int sigprocmask(int, const scope sigset_t*, sigset_t*); + int sigsuspend(const scope sigset_t*); + int sigwait(const scope sigset_t*, int*); +} +else version (NetBSD) +{ + int kill(pid_t, int); + int __sigaction14(int, const scope sigaction_t*, sigaction_t*); + int __sigaddset14(sigset_t*, int); + int __sigdelset14(sigset_t*, int); + int __sigemptyset14(sigset_t *); + int __sigfillset14(sigset_t *); + int __sigismember14(const scope sigset_t*, int); + int __sigpending14(sigset_t *); + int __sigprocmask14(int, const scope sigset_t*, sigset_t*); + int __sigsuspend14(const scope sigset_t*); + int sigwait(const scope sigset_t*, int*); + alias __sigaction14 sigaction; + alias __sigaddset14 sigaddset; + alias __sigdelset14 sigdelset; + alias __sigemptyset14 sigemptyset; + alias __sigfillset14 sigfillset; + alias __sigismember14 sigismember; + alias __sigpending14 sigpending; + alias __sigprocmask14 sigprocmask; + alias __sigsuspend14 sigsuspend; +} +else version (OpenBSD) +{ + int kill(pid_t, int); + int sigaction(int, const scope sigaction_t*, sigaction_t*); + int sigaddset(sigset_t*, int); + int sigdelset(sigset_t*, int); + int sigemptyset(sigset_t *); + int sigfillset(sigset_t *); + int sigismember(const scope sigset_t*, int); + int sigpending(sigset_t *); + int sigprocmask(int, const scope sigset_t*, sigset_t*); + int sigsuspend(const scope sigset_t*); + int sigwait(const scope sigset_t*, int*); +} +else version (DragonFlyBSD) +{ + int kill(pid_t, int); + int sigaction(int, const scope sigaction_t*, sigaction_t*); + int sigaddset(sigset_t*, int); + int sigdelset(sigset_t*, int); + int sigemptyset(sigset_t *); + int sigfillset(sigset_t *); + int sigismember(const scope sigset_t*, int); + int sigpending(sigset_t *); + int sigprocmask(int, const scope sigset_t*, sigset_t*); + int sigsuspend(const scope sigset_t*); + int sigwait(const scope sigset_t*, int*); +} +else version (Solaris) +{ int kill(pid_t, int); int sigaction(int, const scope sigaction_t*, sigaction_t*); int sigaddset(sigset_t*, int); @@ -1467,101 +1526,15 @@ else version (CRuntime_Bionic) import core.stdc.string : memset; version (X86) - { - alias uint sigset_t; enum int LONG_BIT = 32; - } else version (ARM) - { - alias uint sigset_t; enum int LONG_BIT = 32; - } else version (AArch64) - { - struct sigset_t { ulong[1] sig; } enum int LONG_BIT = 64; - } else version (X86_64) - { - alias ulong sigset_t; enum int LONG_BIT = 64; - } else - { static assert(false, "Architecture not supported."); - } - - enum SIG_BLOCK = 0; - enum SIG_UNBLOCK = 1; - enum SIG_SETMASK = 2; - - private enum SI_MAX_SIZE = 128; - private enum SI_PAD_SIZE = ((SI_MAX_SIZE / int.sizeof) - 3); - - struct siginfo_t - { - int si_signo; - int si_errno; - int si_code; - - union _sifields_t - { - int[SI_PAD_SIZE] _pad; - - struct _kill_t - { - pid_t _pid; - uid_t _uid; - } _kill_t _kill; - - struct _timer_t - { - timer_t _tid; - int _overrun; - sigval _sigval; - int _sys_private; - } _timer_t _timer; - - struct _rt_t - { - pid_t _pid; - uid_t _uid; - sigval _sigval; - } _rt_t _rt; - - struct _sigchild_t - { - pid_t _pid; - uid_t _uid; - int _status; - clock_t _utime; - clock_t _stime; - } _sigchild_t _sigchld; - - struct _sigfault_t - { - void* _addr; - } _sigfault_t _sigfault; - - struct _sigpoll_t - { - c_long _band; - int _fd; - } _sigpoll_t _sigpoll; - } _sifields_t _sifields; - } - - enum - { - SI_TKILL = -6, - SI_SIGIO, - SI_ASYNCIO, - SI_MESGQ, - SI_TIMER, - SI_QUEUE, - SI_USER, - SI_KERNEL = 0x80 - } int kill(pid_t, int); int sigaction(int, const scope sigaction_t*, sigaction_t*); @@ -1601,111 +1574,6 @@ else version (CRuntime_Bionic) } else version (CRuntime_Musl) { - struct sigset_t - { - c_ulong[128/c_long.sizeof] __bits; - } - - version (MIPS_Any) - { - enum SIG_BLOCK = 1; - enum SIG_UNBLOCK = 2; - enum SIG_SETMASK = 3; - } - else - { - enum SIG_BLOCK = 0; - enum SIG_UNBLOCK = 1; - enum SIG_SETMASK = 2; - } - - struct siginfo_t - { - int si_signo; - version (MIPS_Any) // __SI_SWAP_ERRNO_CODE - { - int si_code; - int si_errno; - } - else - { - int si_errno; - int si_code; - } - union __si_fields_t - { - char[128 - 2*int.sizeof - c_long.sizeof] __pad = 0; - struct __si_common_t - { - union __first_t - { - struct __piduid_t - { - pid_t si_pid; - uid_t si_uid; - } - __piduid_t __piduid; - - struct __timer_t - { - int si_timerid; - int si_overrun; - } - __timer_t __timer; - } - __first_t __first; - - union __second_t - { - sigval si_value; - struct __sigchld_t - { - int si_status; - clock_t si_utime; - clock_t si_stime; - } - __sigchld_t __sigchld; - } - __second_t __second; - } - __si_common_t __si_common; - - struct __sigfault_t - { - void *si_addr; - short si_addr_lsb; - union __first_t - { - struct __addr_bnd_t - { - void *si_lower; - void *si_upper; - } - __addr_bnd_t __addr_bnd; - uint si_pkey; - } - __first_t __first; - } - __sigfault_t __sigfault; - - struct __sigpoll_t - { - c_long si_band; - int si_fd; - } - __sigpoll_t __sigpoll; - - struct __sigsys_t - { - void *si_call_addr; - int si_syscall; - uint si_arch; - } - __sigsys_t __sigsys; - } - __si_fields_t __si_fields; - } - int kill(pid_t, int); int sigaction(int, const scope sigaction_t*, sigaction_t*); int sigaddset(sigset_t*, int); @@ -1720,235 +1588,6 @@ else version (CRuntime_Musl) } else version (CRuntime_UClibc) { - enum SIG_HOLD = cast(sigfn_t2) 2; - - version (MIPS32) - private enum _SIGSET_NWORDS = 128 / (8 * c_ulong.sizeof); - else - private enum _SIGSET_NWORDS = 64 / (8 * c_ulong.sizeof); - - struct sigset_t - { - c_ulong[_SIGSET_NWORDS] __val; - } - - enum SA_NOCLDSTOP = 1; - - enum SIG_BLOCK = 0; - enum SIG_UNBLOCK = 1; - enum SIG_SETMASK = 2; - - private enum __SI_MAX_SIZE = 128; - - static if ( __WORDSIZE == 64 ) - { - private enum __SI_PAD_SIZE = ((__SI_MAX_SIZE / int.sizeof) - 4); - } - else - { - private enum __SI_PAD_SIZE = ((__SI_MAX_SIZE / int.sizeof) - 3); - } - - version (ARM) version = siginfo_common; - else version (X86_64) version = siginfo_common; - - version (siginfo_common) - { - struct siginfo_t - { - int si_signo; // Signal number - int si_errno; // If non-zero, an errno value associated with - // this signal, as defined in <errno.h> - int si_code; // Signal code - - union _sifields_t - { - int[__SI_PAD_SIZE] _pad; - - // kill() - struct _kill_t - { - pid_t si_pid; // Sending process ID - uid_t si_uid; // Real user ID of sending process - } _kill_t _kill; - - // POSIX.1b timers. - struct _timer_t - { - int si_tid; // Timer ID - int si_overrun; // Overrun count - sigval si_sigval; // Signal value - } _timer_t _timer; - - // POSIX.1b signals - struct _rt_t - { - pid_t si_pid; // Sending process ID - uid_t si_uid; // Real user ID of sending process - sigval si_sigval; // Signal value - } _rt_t _rt; - - // SIGCHLD - struct _sigchild_t - { - pid_t si_pid; // Which child - uid_t si_uid; // Real user ID of sending process - int si_status; // Exit value or signal - clock_t si_utime; - clock_t si_stime; - } _sigchild_t _sigchld; - - // SIGILL, SIGFPE, SIGSEGV, SIGBUS - struct _sigfault_t - { - void* si_addr; // Faulting insn/memory ref - } _sigfault_t _sigfault; - - // SIGPOLL - struct _sigpoll_t - { - c_long si_band; // Band event for SIGPOLL; - int si_fd; - } _sigpoll_t _sigpoll; - - // SIGSYS - struct _sigsys_t - { - void* _call_addr; // Calling user insn. - int _syscall; // Triggering system call number. - uint _arch; // AUDIT_ARCH_* of syscall. - } _sigsys_t _sigsys; - - } _sifields_t _sifields; - - nothrow @nogc: - @property ref pid_t si_pid()() { return _sifields._kill.si_pid; } - @property ref uid_t si_uid()() { return _sifields._kill.si_uid; } - @property ref int si_timerid()() { return _sifields._timer.si_tid;} - @property ref int si_overrun()() { return _sifields._timer.si_overrun; } - @property ref int si_status()() { return _sifields._sigchld.si_status; } - @property ref clock_t si_utime()() { return _sifields._sigchld.si_utime; } - @property ref clock_t si_stime()() { return _sifields._sigchld.si_stime; } - @property ref sigval si_value()() { return _sifields._rt.si_sigval; } - @property ref int si_int()() { return _sifields._rt.si_sigval.sival_int; } - @property ref void* si_ptr()() { return _sifields._rt.si_sigval.sival_ptr; } - @property ref void* si_addr()() { return _sifields._sigfault.si_addr; } - @property ref c_long si_band()() { return _sifields._sigpoll.si_band; } - @property ref int si_fd()() { return _sifields._sigpoll.si_fd; } - @property ref void* si_call_addr()() { return _sifields._sigsys._call_addr; } - @property ref int si_syscall()() { return _sifields._sigsys._syscall; } - @property ref uint si_arch()() { return _sifields._sigsys._arch; } - } - } - else version (MIPS32) - { - struct siginfo_t - { - int si_signo; // Signal number - int si_errno; // If non-zero, an errno value associated with - // this signal, as defined in <errno.h> - int si_code; // Signal code - - int[__SI_MAX_SIZE / int.sizeof - __SI_PAD_SIZE - 3] __pad0; - - union _sifields_t - { - int[__SI_PAD_SIZE] _pad; - - // kill() - struct _kill_t - { - pid_t si_pid; // Sending process ID - uid_t si_uid; // Real user ID of sending process - } _kill_t _kill; - - // POSIX.1b timers. - struct _timer_t - { - int si_tid; // Timer ID - int si_overrun; // Overrun count - sigval si_sigval; // Signal value - } _timer_t _timer; - - // POSIX.1b signals - struct _rt_t - { - pid_t si_pid; // Sending process ID - uid_t si_uid; // Real user ID of sending process - sigval si_sigval; // Signal value - } _rt_t _rt; - - // SIGCHLD - struct _sigchild_t - { - pid_t si_pid; // Which child - uid_t si_uid; // Real user ID of sending process - int si_status; // Exit value or signal - clock_t si_utime; - clock_t si_stime; - } _sigchild_t _sigchld; - - // SIGILL, SIGFPE, SIGSEGV, SIGBUS - struct _sigfault_t - { - void* si_addr; // Faulting insn/memory ref - short si_addr_lsb; - } _sigfault_t _sigfault; - - // SIGPOLL - struct _sigpoll_t - { - c_long si_band; // Band event for SIGPOLL; - int si_fd; - } _sigpoll_t _sigpoll; - - // SIGSYS - struct _sigsys_t - { - void* _call_addr; // Calling user insn. - int _syscall; // Triggering system call number. - uint _arch; // AUDIT_ARCH_* of syscall. - } _sigsys_t _sigsys; - - } _sifields_t _sifields; - - nothrow @nogc: - @property ref pid_t si_pid()() { return _sifields._kill.si_pid; } - @property ref uid_t si_uid()() { return _sifields._kill.si_uid; } - @property ref int si_timerid()() { return _sifields._timer.si_tid;} - @property ref int si_overrun()() { return _sifields._timer.si_overrun; } - @property ref int si_status()() { return _sifields._sigchld.si_status; } - @property ref clock_t si_utime()() { return _sifields._sigchld.si_utime; } - @property ref clock_t si_stime()() { return _sifields._sigchld.si_stime; } - @property ref sigval si_value()() { return _sifields._rt.si_sigval; } - @property ref int si_int()() { return _sifields._rt.si_sigval.sival_int; } - @property ref void* si_ptr()() { return _sifields._rt.si_sigval.sival_ptr; } - @property ref void* si_addr()() { return _sifields._sigfault.si_addr; } - @property ref c_long si_band()() { return _sifields._sigpoll.si_band; } - @property ref int si_fd()() { return _sifields._sigpoll.si_fd; } - @property ref void* si_call_addr()() { return _sifields._sigsys._call_addr; } - @property ref int si_syscall()() { return _sifields._sigsys._syscall; } - @property ref uint si_arch()() { return _sifields._sigsys._arch; } - } - } - else - { - static assert(false, "Architecture not supported."); - } - - enum - { - SI_ASYNCNL = -60, - SI_TKILL = -6, - SI_SIGIO, - SI_ASYNCIO, - SI_MESGQ, - SI_TIMER, - SI_QUEUE, - SI_USER, - SI_KERNEL = 0x80 - } - int kill(pid_t, int); int sigaction(int, const scope sigaction_t*, sigaction_t*); int sigaddset(sigset_t*, int); @@ -1985,26 +1624,6 @@ SA_RESTART SA_SIGINFO SA_NOCLDWAIT SA_NODEFER -SS_ONSTACK -SS_DISABLE -MINSIGSTKSZ -SIGSTKSZ - -ucontext_t // from ucontext -mcontext_t // from ucontext - -struct stack_t -{ - void* ss_sp; - size_t ss_size; - int ss_flags; -} - -struct sigstack -{ - int ss_onstack; - void* ss_sp; -} ILL_ILLOPC ILL_ILLOPN @@ -2047,20 +1666,9 @@ POLL_MSG POLL_ERR POLL_PRI POLL_HUP - -sigfn_t bsd_signal(int sig, sigfn_t func); -sigfn_t sigset(int sig, sigfn_t func); - -int killpg(pid_t, int); -int sigaltstack(const scope stack_t*, stack_t*); -int sighold(int); -int sigignore(int); -int siginterrupt(int, int); -int sigpause(int); -int sigrelse(int); */ -version (CRuntime_Glibc) +version (linux) { version (X86_Any) { @@ -2145,33 +1753,29 @@ version (CRuntime_Glibc) else static assert(0, "unimplemented"); - enum SA_ONSTACK = 0x08000000; - enum SA_RESETHAND = 0x80000000; - enum SA_RESTART = 0x10000000; - enum SA_SIGINFO = 4; - enum SA_NOCLDWAIT = 2; - enum SA_NODEFER = 0x40000000; - enum SS_ONSTACK = 1; - enum SS_DISABLE = 2; - enum MINSIGSTKSZ = 2048; - enum SIGSTKSZ = 8192; - - //ucontext_t (defined in core.sys.posix.ucontext) - //mcontext_t (defined in core.sys.posix.ucontext) - - struct stack_t + version (MIPS_Any) { - void* ss_sp; - int ss_flags; - size_t ss_size; + enum SA_ONSTACK = 0x08000000; + enum SA_RESETHAND = 0x80000000; + enum SA_RESTART = 0x10000000; + enum SA_SIGINFO = 8; + enum SA_NOCLDWAIT = 0x10000; + enum SA_NODEFER = 0x40000000; } - - struct sigstack + else { - void* ss_sp; - int ss_onstack; + enum SA_ONSTACK = 0x08000000; + enum SA_RESETHAND = 0x80000000; + enum SA_RESTART = 0x10000000; + enum SA_SIGINFO = 4; + enum SA_NOCLDWAIT = 2; + enum SA_NODEFER = 0x40000000; } + enum SA_NOMASK = SA_NODEFER; + enum SA_ONESHOT = SA_RESETHAND; + enum SA_STACK = SA_ONSTACK; + enum { ILL_ILLOPC = 1, @@ -2234,22 +1838,6 @@ version (CRuntime_Glibc) POLL_PRI, POLL_HUP } - - sigfn_t bsd_signal(int sig, sigfn_t func); - sigfn_t sigset(int sig, sigfn_t func); - - nothrow: - @nogc: - sigfn_t2 bsd_signal(int sig, sigfn_t2 func); - sigfn_t2 sigset(int sig, sigfn_t2 func); - - int killpg(pid_t, int); - int sigaltstack(const scope stack_t*, stack_t*); - int sighold(int); - int sigignore(int); - int siginterrupt(int, int); - int sigpause(int); - int sigrelse(int); } else version (Darwin) { @@ -2267,26 +1855,6 @@ else version (Darwin) enum SA_SIGINFO = 0x0040; enum SA_NOCLDWAIT = 0x0020; enum SA_NODEFER = 0x0010; - enum SS_ONSTACK = 0x0001; - enum SS_DISABLE = 0x0004; - enum MINSIGSTKSZ = 32768; - enum SIGSTKSZ = 131072; - - //ucontext_t (defined in core.sys.posix.ucontext) - //mcontext_t (defined in core.sys.posix.ucontext) - - struct stack_t - { - void* ss_sp; - size_t ss_size; - int ss_flags; - } - - struct sigstack - { - void* ss_sp; - int ss_onstack; - } enum ILL_ILLOPC = 1; enum ILL_ILLOPN = 4; @@ -2344,22 +1912,6 @@ else version (Darwin) POLL_PRI, POLL_HUP } - - sigfn_t bsd_signal(int sig, sigfn_t func); - sigfn_t sigset(int sig, sigfn_t func); - - nothrow: - @nogc: - sigfn_t2 bsd_signal(int sig, sigfn_t2 func); - sigfn_t2 sigset(int sig, sigfn_t2 func); - - int killpg(pid_t, int); - int sigaltstack(const scope stack_t*, stack_t*); - int sighold(int); - int sigignore(int); - int siginterrupt(int, int); - int sigpause(int); - int sigrelse(int); } else version (FreeBSD) { @@ -2383,31 +1935,6 @@ else version (FreeBSD) enum { - SS_ONSTACK = 0x0001, - SS_DISABLE = 0x0004, - } - - enum MINSIGSTKSZ = 512 * 4; - enum SIGSTKSZ = (MINSIGSTKSZ + 32768); - - //ucontext_t (defined in core.sys.posix.ucontext) - //mcontext_t (defined in core.sys.posix.ucontext) - - struct stack_t - { - void* ss_sp; - size_t ss_size; - int ss_flags; - } - - struct sigstack - { - void* ss_sp; - int ss_onstack; - } - - enum - { ILL_ILLOPC = 1, ILL_ILLOPN, ILL_ILLADR, @@ -2468,22 +1995,6 @@ else version (FreeBSD) POLL_PRI, POLL_HUP, } - - //sigfn_t bsd_signal(int sig, sigfn_t func); - sigfn_t sigset(int sig, sigfn_t func); - - nothrow: - @nogc: - //sigfn_t2 bsd_signal(int sig, sigfn_t2 func); - sigfn_t2 sigset(int sig, sigfn_t2 func); - - int killpg(pid_t, int); - int sigaltstack(const scope stack_t*, stack_t*); - int sighold(int); - int sigignore(int); - int siginterrupt(int, int); - int sigpause(int); - int sigrelse(int); } else version (NetBSD) { @@ -2507,31 +2018,6 @@ else version (NetBSD) enum { - SS_ONSTACK = 0x0001, - SS_DISABLE = 0x0004, - } - - enum MINSIGSTKSZ = 8192; - enum SIGSTKSZ = (MINSIGSTKSZ + 32768); - - //ucontext_t (defined in core.sys.posix.ucontext) - //mcontext_t (defined in core.sys.posix.ucontext) - - struct stack_t - { - void* ss_sp; - size_t ss_size; - int ss_flags; - } - - struct sigstack - { - void* ss_sp; - int ss_onstack; - } - - enum - { ILL_ILLOPC = 1, ILL_ILLOPN, ILL_ILLADR, @@ -2592,22 +2078,6 @@ else version (NetBSD) POLL_PRI, POLL_HUP, } - - //sigfn_t bsd_signal(int sig, sigfn_t func); - sigfn_t sigset(int sig, sigfn_t func); - - nothrow: - @nogc: - //sigfn_t2 bsd_signal(int sig, sigfn_t2 func); - sigfn_t2 sigset(int sig, sigfn_t2 func); - - int killpg(pid_t, int); - int sigaltstack(const scope stack_t*, stack_t*); - int sighold(int); - int sigignore(int); - int siginterrupt(int, int); - int sigpause(int); - int sigrelse(int); } else version (OpenBSD) { @@ -2631,25 +2101,6 @@ else version (OpenBSD) enum { - SS_ONSTACK = 0x0001, - SS_DISABLE = 0x0004, - } - - enum MINSIGSTKSZ = 8192; - enum SIGSTKSZ = (MINSIGSTKSZ + 32768); - - //ucontext_t (defined in core.sys.posix.ucontext) - //mcontext_t (defined in core.sys.posix.ucontext) - - struct stack_t - { - void* ss_sp; - size_t ss_size; - int ss_flags; - } - - enum - { ILL_ILLOPC = 1, ILL_ILLOPN, ILL_ILLADR, @@ -2717,13 +2168,6 @@ else version (OpenBSD) POLL_HUP, NSIGPOLL = POLL_HUP, } - - nothrow: - @nogc: - int killpg(pid_t, int); - int sigaltstack(const scope stack_t*, stack_t*); - int siginterrupt(int, int); - int sigpause(int); } else version (DragonFlyBSD) { @@ -2747,31 +2191,6 @@ else version (DragonFlyBSD) enum { - SS_ONSTACK = 0x0001, - SS_DISABLE = 0x0004, - } - - enum MINSIGSTKSZ = 8192; - enum SIGSTKSZ = (MINSIGSTKSZ + 32768); - - //ucontext_t (defined in core.sys.posix.ucontext) - //mcontext_t (defined in core.sys.posix.ucontext) - - struct stack_t - { - void* ss_sp; - size_t ss_size; - int ss_flags; - } - - struct sigstack - { - void* ss_sp; - int ss_onstack; - } - - enum - { ILL_ILLOPC = 1, ILL_ILLOPN, ILL_ILLADR, @@ -2832,26 +2251,11 @@ else version (DragonFlyBSD) POLL_PRI, POLL_HUP, } - - //sigfn_t bsd_signal(int sig, sigfn_t func); - sigfn_t sigset(int sig, sigfn_t func); - - nothrow: - @nogc: - //sigfn_t2 bsd_signal(int sig, sigfn_t2 func); - sigfn_t2 sigset(int sig, sigfn_t2 func); - - int killpg(pid_t, int); - int sigaltstack(const scope stack_t*, stack_t*); - int sighold(int); - int sigignore(int); - int siginterrupt(int, int); - int sigpause(int); - int sigrelse(int); } else version (Solaris) { enum SIGPOLL = 22; + enum SIGIO = SIGPOLL; enum SIGPROF = 29; enum SIGSYS = 12; enum SIGTRAP = 5; @@ -2871,28 +2275,6 @@ else version (Solaris) enum { - SS_ONSTACK = 0x0001, - SS_DISABLE = 0x0002, - } - - enum MINSIGSTKSZ = 2048; - enum SIGSTKSZ = 8192; - - struct stack_t - { - void* ss_sp; - size_t ss_size; - int ss_flags; - } - - struct sigstack - { - void* ss_sp; - int ss_onstack; - } - - enum - { ILL_ILLOPC = 1, ILL_ILLOPN, ILL_ILLADR, @@ -2958,11 +2340,75 @@ else version (Solaris) POLL_PRI, POLL_HUP, } +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +SS_ONSTACK +SS_DISABLE +MINSIGSTKSZ +SIGSTKSZ + +ucontext_t // from ucontext +mcontext_t // from ucontext + +struct stack_t +{ + void* ss_sp; + size_t ss_size; + int ss_flags; +} + +struct sigstack +{ + int ss_onstack; + void* ss_sp; +} + +sigfn_t bsd_signal(int sig, sigfn_t func); +sigfn_t sigset(int sig, sigfn_t func); + +int killpg(pid_t, int); +int sigaltstack(const scope stack_t*, stack_t*); +int sighold(int); +int sigignore(int); +int siginterrupt(int, int); +int sigpause(int); +int sigrelse(int); +*/ + +version (CRuntime_Glibc) +{ + enum SS_ONSTACK = 1; + enum SS_DISABLE = 2; + enum MINSIGSTKSZ = 2048; + enum SIGSTKSZ = 8192; + + //ucontext_t (defined in core.sys.posix.ucontext) + //mcontext_t (defined in core.sys.posix.ucontext) + + struct stack_t + { + void* ss_sp; + int ss_flags; + size_t ss_size; + } + struct sigstack + { + void* ss_sp; + int ss_onstack; + } + + sigfn_t bsd_signal(int sig, sigfn_t func); sigfn_t sigset(int sig, sigfn_t func); nothrow: @nogc: + sigfn_t2 bsd_signal(int sig, sigfn_t2 func); sigfn_t2 sigset(int sig, sigfn_t2 func); int killpg(pid_t, int); @@ -2973,144 +2419,266 @@ else version (Solaris) int sigpause(int); int sigrelse(int); } -else version (CRuntime_Bionic) +else version (Darwin) { - enum SIGPOLL = 29; - enum SIGPROF = 27; - enum SIGSYS = 31; - enum SIGTRAP = 5; - enum SIGVTALRM = 26; - enum SIGXCPU = 24; - enum SIGXFSZ = 25; - - enum SA_ONSTACK = 0x08000000; - enum SA_RESETHAND = 0x80000000; - enum SA_RESTART = 0x10000000; - enum SA_SIGINFO = 4; - enum SA_NOCLDWAIT = 2; - enum SA_NODEFER = 0x40000000; - enum SS_ONSTACK = 1; - enum SS_DISABLE = 2; - enum MINSIGSTKSZ = 2048; - enum SIGSTKSZ = 8192; + enum SS_ONSTACK = 0x0001; + enum SS_DISABLE = 0x0004; + enum MINSIGSTKSZ = 32768; + enum SIGSTKSZ = 131072; + + //ucontext_t (defined in core.sys.posix.ucontext) + //mcontext_t (defined in core.sys.posix.ucontext) struct stack_t { void* ss_sp; - int ss_flags; size_t ss_size; + int ss_flags; } - enum + struct sigstack { - ILL_ILLOPC = 1, - ILL_ILLOPN, - ILL_ILLADR, - ILL_ILLTRP, - ILL_PRVOPC, - ILL_PRVREG, - ILL_COPROC, - ILL_BADSTK + void* ss_sp; + int ss_onstack; } + sigfn_t bsd_signal(int sig, sigfn_t func); + sigfn_t sigset(int sig, sigfn_t func); + + nothrow: + @nogc: + sigfn_t2 bsd_signal(int sig, sigfn_t2 func); + sigfn_t2 sigset(int sig, sigfn_t2 func); + + int killpg(pid_t, int); + int sigaltstack(const scope stack_t*, stack_t*); + int sighold(int); + int sigignore(int); + int siginterrupt(int, int); + int sigpause(int); + int sigrelse(int); +} +else version (FreeBSD) +{ enum { - FPE_INTDIV = 1, - FPE_INTOVF, - FPE_FLTDIV, - FPE_FLTOVF, - FPE_FLTUND, - FPE_FLTRES, - FPE_FLTINV, - FPE_FLTSUB + SS_ONSTACK = 0x0001, + SS_DISABLE = 0x0004, } - enum + enum MINSIGSTKSZ = 512 * 4; + enum SIGSTKSZ = (MINSIGSTKSZ + 32768); + + //ucontext_t (defined in core.sys.posix.ucontext) + //mcontext_t (defined in core.sys.posix.ucontext) + + struct stack_t { - SEGV_MAPERR = 1, - SEGV_ACCERR + void* ss_sp; + size_t ss_size; + int ss_flags; } - enum + struct sigstack { - BUS_ADRALN = 1, - BUS_ADRERR, - BUS_OBJERR + void* ss_sp; + int ss_onstack; } + //sigfn_t bsd_signal(int sig, sigfn_t func); + sigfn_t sigset(int sig, sigfn_t func); + + nothrow: + @nogc: + //sigfn_t2 bsd_signal(int sig, sigfn_t2 func); + sigfn_t2 sigset(int sig, sigfn_t2 func); + + int killpg(pid_t, int); + int sigaltstack(const scope stack_t*, stack_t*); + int sighold(int); + int sigignore(int); + int siginterrupt(int, int); + int sigpause(int); + int sigrelse(int); +} +else version (NetBSD) +{ enum { - TRAP_BRKPT = 1, - TRAP_TRACE + SS_ONSTACK = 0x0001, + SS_DISABLE = 0x0004, } + enum MINSIGSTKSZ = 8192; + enum SIGSTKSZ = (MINSIGSTKSZ + 32768); + + //ucontext_t (defined in core.sys.posix.ucontext) + //mcontext_t (defined in core.sys.posix.ucontext) + + struct stack_t + { + void* ss_sp; + size_t ss_size; + int ss_flags; + } + + struct sigstack + { + void* ss_sp; + int ss_onstack; + } + + //sigfn_t bsd_signal(int sig, sigfn_t func); + sigfn_t sigset(int sig, sigfn_t func); + + nothrow: + @nogc: + //sigfn_t2 bsd_signal(int sig, sigfn_t2 func); + sigfn_t2 sigset(int sig, sigfn_t2 func); + + int killpg(pid_t, int); + int sigaltstack(const scope stack_t*, stack_t*); + int sighold(int); + int sigignore(int); + int siginterrupt(int, int); + int sigpause(int); + int sigrelse(int); +} +else version (OpenBSD) +{ enum { - CLD_EXITED = 1, - CLD_KILLED, - CLD_DUMPED, - CLD_TRAPPED, - CLD_STOPPED, - CLD_CONTINUED + SS_ONSTACK = 0x0001, + SS_DISABLE = 0x0004, } + enum MINSIGSTKSZ = 8192; + enum SIGSTKSZ = (MINSIGSTKSZ + 32768); + + //ucontext_t (defined in core.sys.posix.ucontext) + //mcontext_t (defined in core.sys.posix.ucontext) + + struct stack_t + { + void* ss_sp; + size_t ss_size; + int ss_flags; + } + + nothrow: + @nogc: + int killpg(pid_t, int); + int sigaltstack(const scope stack_t*, stack_t*); + int siginterrupt(int, int); + int sigpause(int); +} +else version (DragonFlyBSD) +{ enum { - POLL_IN = 1, - POLL_OUT, - POLL_MSG, - POLL_ERR, - POLL_PRI, - POLL_HUP + SS_ONSTACK = 0x0001, + SS_DISABLE = 0x0004, } - sigfn_t bsd_signal(int, sigfn_t); + enum MINSIGSTKSZ = 8192; + enum SIGSTKSZ = (MINSIGSTKSZ + 32768); + + //ucontext_t (defined in core.sys.posix.ucontext) + //mcontext_t (defined in core.sys.posix.ucontext) + + struct stack_t + { + void* ss_sp; + size_t ss_size; + int ss_flags; + } + + struct sigstack + { + void* ss_sp; + int ss_onstack; + } + + //sigfn_t bsd_signal(int sig, sigfn_t func); + sigfn_t sigset(int sig, sigfn_t func); nothrow: @nogc: - sigfn_t2 bsd_signal(int, sigfn_t2); + //sigfn_t2 bsd_signal(int sig, sigfn_t2 func); + sigfn_t2 sigset(int sig, sigfn_t2 func); - int killpg(int, int); + int killpg(pid_t, int); int sigaltstack(const scope stack_t*, stack_t*); + int sighold(int); + int sigignore(int); int siginterrupt(int, int); + int sigpause(int); + int sigrelse(int); } -else version (CRuntime_Musl) +else version (Solaris) { - version (MIPS_Any) + enum { - enum SIGPOLL = 22; - enum SIGPROF = 29; - enum SIGSYS = 12; - enum SIGTRAP = 5; - enum SIGVTALRM = 28; - enum SIGXCPU = 30; - enum SIGXFSZ = 31; + SS_ONSTACK = 0x0001, + SS_DISABLE = 0x0002, + } - enum SA_ONSTACK = 0x08000000; - enum SA_RESETHAND = 0x80000000; - enum SA_RESTART = 0x10000000; - enum SA_SIGINFO = 8; - enum SA_NOCLDWAIT = 0x10000; - enum SA_NODEFER = 0x40000000; + enum MINSIGSTKSZ = 2048; + enum SIGSTKSZ = 8192; + + struct stack_t + { + void* ss_sp; + size_t ss_size; + int ss_flags; } - else + + struct sigstack { - enum SIGPOLL = 29; - enum SIGPROF = 27; - enum SIGSYS = 31; - enum SIGTRAP = 5; - enum SIGVTALRM = 26; - enum SIGXCPU = 24; - enum SIGXFSZ = 25; + void* ss_sp; + int ss_onstack; + } - enum SA_ONSTACK = 0x08000000; - enum SA_RESETHAND = 0x80000000; - enum SA_RESTART = 0x10000000; - enum SA_SIGINFO = 4; - enum SA_NOCLDWAIT = 2; - enum SA_NODEFER = 0x40000000; + sigfn_t sigset(int sig, sigfn_t func); + + nothrow: + @nogc: + sigfn_t2 sigset(int sig, sigfn_t2 func); + + int killpg(pid_t, int); + int sigaltstack(const scope stack_t*, stack_t*); + int sighold(int); + int sigignore(int); + int siginterrupt(int, int); + int sigpause(int); + int sigrelse(int); +} +else version (CRuntime_Bionic) +{ + enum SS_ONSTACK = 1; + enum SS_DISABLE = 2; + enum MINSIGSTKSZ = 2048; + enum SIGSTKSZ = 8192; + + struct stack_t + { + void* ss_sp; + int ss_flags; + size_t ss_size; } + sigfn_t bsd_signal(int, sigfn_t); + + nothrow: + @nogc: + sigfn_t2 bsd_signal(int, sigfn_t2); + + int killpg(int, int); + int sigaltstack(const scope stack_t*, stack_t*); + int siginterrupt(int, int); +} +else version (CRuntime_Musl) +{ enum SS_ONSTACK = 1; enum SS_DISABLE = 2; @@ -3169,69 +2737,6 @@ else version (CRuntime_Musl) } } - enum - { - ILL_ILLOPC = 1, - ILL_ILLOPN, - ILL_ILLADR, - ILL_ILLTRP, - ILL_PRVOPC, - ILL_PRVREG, - ILL_COPROC, - ILL_BADSTK - } - - enum - { - FPE_INTDIV = 1, - FPE_INTOVF, - FPE_FLTDIV, - FPE_FLTOVF, - FPE_FLTUND, - FPE_FLTRES, - FPE_FLTINV, - FPE_FLTSUB - } - - enum - { - SEGV_MAPERR = 1, - SEGV_ACCERR - } - - enum - { - BUS_ADRALN = 1, - BUS_ADRERR, - BUS_OBJERR - } - - enum - { - TRAP_BRKPT = 1, - TRAP_TRACE - } - - enum - { - CLD_EXITED = 1, - CLD_KILLED, - CLD_DUMPED, - CLD_TRAPPED, - CLD_STOPPED, - CLD_CONTINUED - } - - enum - { - POLL_IN = 1, - POLL_OUT, - POLL_MSG, - POLL_ERR, - POLL_PRI, - POLL_HUP - } - sigfn_t bsd_signal(int sig, sigfn_t func); sigfn_t sigset(int sig, sigfn_t func); @@ -3250,77 +2755,11 @@ else version (CRuntime_Musl) } else version (CRuntime_UClibc) { - version (X86_64) - { - enum SIGTRAP = 5; - enum SIGIOT = 6; - enum SIGSTKFLT = 16; - enum SIGCLD = SIGCHLD; - enum SIGXCPU = 24; - enum SIGXFSZ = 25; - enum SIGVTALRM = 26; - enum SIGPROF = 27; - enum SIGWINCH = 28; - enum SIGPOLL = SIGIO; - enum SIGIO = 29; - enum SIGPWR = 30; - enum SIGSYS = 31; - enum SIGUNUSED = 31; - } - else version (MIPS32) - { - enum SIGTRAP = 5; - enum SIGIOT = 6; - enum SIGEMT = 7; - enum SIGFPE = 8; - enum SIGSYS = 12; - enum SIGCLD = SIGCHLD; - enum SIGPWR = 19; - enum SIGWINCH = 20; - enum SIGIO = 22; - enum SIGPOLL = SIGIO; - enum SIGVTALRM = 28; - enum SIGPROF = 29; - enum SIGXCPU = 30; - enum SIGXFSZ = 31; - } - else version (ARM) - { - enum SIGTRAP = 5; - enum SIGIOT = 6; - enum SIGSTKFLT = 16; - enum SIGCLD = SIGCHLD; - enum SIGXCPU = 24; - enum SIGXFSZ = 25; - enum SIGVTALRM = 26; - enum SIGPROF = 27; - enum SIGWINCH = 28; - enum SIGPOLL = SIGIO; - enum SIGIO = 29; - enum SIGPWR = 30; - enum SIGSYS = 31; - enum SIGUNUSED = 31; - } - else - static assert(0, "unimplemented"); - - enum SA_ONSTACK = 0x08000000; - enum SA_RESETHAND = 0x80000000; - enum SA_RESTART = 0x10000000; - enum SA_SIGINFO = 4; - enum SA_NOCLDWAIT = 2; - enum SA_NODEFER = 0x40000000; enum SS_ONSTACK = 1; enum SS_DISABLE = 2; enum MINSIGSTKSZ = 2048; enum SIGSTKSZ = 8192; - enum SA_INTERRUPT = 0x20000000; - - enum SA_NOMASK = SA_NODEFER; - enum SA_ONESHOT = SA_RESETHAND; - enum SA_STACK = SA_ONSTACK; - version (MIPS32) { struct stack_t @@ -3346,76 +2785,6 @@ else version (CRuntime_UClibc) int ss_onstack; } - // `si_code' values for SIGILL signal. - enum - { - ILL_ILLOPC = 1, // Illegal opcode. - ILL_ILLOPN, // Illegal operand. - ILL_ILLADR, // Illegal addressing mode. - ILL_ILLTRP, // Illegal trap. - ILL_PRVOPC, // Privileged opcode. - ILL_PRVREG, // Privileged register. - ILL_COPROC, // Coprocessor error. - ILL_BADSTK // Internal stack error. - } - - // `si_code' values for SIGFPE signal. - enum - { - FPE_INTDIV = 1, // Integer divide by zero. - FPE_INTOVF, // Integer overflow. - FPE_FLTDIV, // Floating point divide by zero. - FPE_FLTOVF, // Floating point overflow. - FPE_FLTUND, // Floating point underflow. - FPE_FLTRES, // Floating point inexact result. - FPE_FLTINV, // Floating point invalid operation. - FPE_FLTSUB // Subscript out of range. - } - - // `si_code' values for SIGSEGV signal. - enum - { - SEGV_MAPERR = 1, // Address not mapped to object. - SEGV_ACCERR // Invalid permissions for mapped object. - } - - // `si_code' values for SIGBUS signal. - enum - { - BUS_ADRALN = 1, // Invalid address alignment. - BUS_ADRERR, // Non-existant physical address. - BUS_OBJERR // Object specific hardware error. - } - - // `si_code' values for SIGTRAP signal. - enum - { - TRAP_BRKPT = 1, // Process breakpoint. - TRAP_TRACE // Process trace trap. - } - - // `si_code' values for SIGCHLD signal. - enum - { - CLD_EXITED = 1, // Child has exited. - CLD_KILLED, // Child was killed. - CLD_DUMPED, // Child terminated abnormally. - CLD_TRAPPED, // Traced child has trapped. - CLD_STOPPED, // Child has stopped. - CLD_CONTINUED // Stopped child has continued. - } - - // `si_code' values for SIGPOLL signal. - enum - { - POLL_IN = 1, // Data input available. - POLL_OUT, // Output buffers available. - POLL_MSG, // Input message available. - POLL_ERR, // I/O error. - POLL_PRI, // High priority input available. - POLL_HUP // Device disconnected. - } - sigfn_t sigset(int sig, sigfn_t func); nothrow: @@ -3524,16 +2893,12 @@ struct sigevent void(*)(sigval) sigev_notify_function; pthread_attr_t* sigev_notify_attributes; } - -int sigqueue(pid_t, int, const sigval); -int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); -int sigwaitinfo(const scope sigset_t*, siginfo_t*); */ nothrow: @nogc: -version (CRuntime_Glibc) +version (linux) { private enum __SIGEV_MAX_SIZE = 64; @@ -3552,22 +2917,18 @@ version (CRuntime_Glibc) int sigev_signo; int sigev_notify; - union _sigev_un_t + union { int[__SIGEV_PAD_SIZE] _pad; pid_t _tid; - struct _sigev_thread_t + struct { - void function(sigval) _function; - void* _attribute; - } _sigev_thread_t _sigev_thread; - } _sigev_un_t _sigev_un; + void function(sigval) sigev_notify_function; + void* sigev_notify_attributes; + } + } } - - int sigqueue(pid_t, int, const sigval); - int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); - int sigwaitinfo(const scope sigset_t*, siginfo_t*); } else version (FreeBSD) { @@ -3576,21 +2937,17 @@ else version (FreeBSD) int sigev_notify; int sigev_signo; sigval sigev_value; - union _sigev_un + union { lwpid_t _threadid; - struct _sigev_thread + struct { - void function(sigval) _function; - void* _attribute; + void function(sigval) sigev_notify_function; + void* sigev_notify_attributes; } c_long[8] __spare__; } } - - int sigqueue(pid_t, int, const sigval); - int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); - int sigwaitinfo(const scope sigset_t*, siginfo_t*); } else version (NetBSD) { @@ -3602,10 +2959,6 @@ else version (NetBSD) void function(sigval) sigev_notify_function; void /* pthread_attr_t */*sigev_notify_attributes; } - - int sigqueue(pid_t, int, const sigval); - int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); - int sigwaitinfo(const scope sigset_t*, siginfo_t*); } else version (OpenBSD) { @@ -3634,10 +2987,6 @@ else version (DragonFlyBSD) _sigval_t sigev_value; void function(_sigval_t) sigev_notify_function; } - - int sigqueue(pid_t, int, const sigval); - int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); - int sigwaitinfo(const scope sigset_t*, siginfo_t*); } else version (Darwin) { @@ -3661,84 +3010,68 @@ else version (Solaris) pthread_attr_t* sigev_notify_attributes; int __sigev_pad2; } +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +int sigqueue(pid_t, int, const sigval); +int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); +int sigwaitinfo(const scope sigset_t*, siginfo_t*); +*/ +nothrow: +@nogc: + +version (CRuntime_Glibc) +{ + int sigqueue(pid_t, int, const sigval); + int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); + int sigwaitinfo(const scope sigset_t*, siginfo_t*); +} +else version (FreeBSD) +{ + int sigqueue(pid_t, int, const sigval); + int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); + int sigwaitinfo(const scope sigset_t*, siginfo_t*); +} +else version (NetBSD) +{ + int sigqueue(pid_t, int, const sigval); + int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); + int sigwaitinfo(const scope sigset_t*, siginfo_t*); +} +else version (OpenBSD) +{ +} +else version (DragonFlyBSD) +{ + int sigqueue(pid_t, int, const sigval); + int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); + int sigwaitinfo(const scope sigset_t*, siginfo_t*); +} +else version (Darwin) +{ +} +else version (Solaris) +{ int sigqueue(pid_t, int, const sigval); int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); int sigwaitinfo(const scope sigset_t*, siginfo_t*); } else version (CRuntime_Bionic) { - private enum __ARCH_SIGEV_PREAMBLE_SIZE = (int.sizeof * 2) + sigval.sizeof; - private enum SIGEV_MAX_SIZE = 64; - private enum SIGEV_PAD_SIZE = (SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE) - / int.sizeof; - - struct sigevent - { - sigval sigev_value; - int sigev_signo; - int sigev_notify; - - union _sigev_un_t - { - int[SIGEV_PAD_SIZE] _pad; - int _tid; - - struct _sigev_thread_t - { - void function(sigval) _function; - void* _attribute; - } _sigev_thread_t _sigev_thread; - } _sigev_un_t _sigev_un; - } } else version (CRuntime_Musl) { - struct sigevent - { - sigval sigev_value; - int sigev_signo; - int sigev_notify; - void function(sigval) sigev_notify_function; - pthread_attr_t *sigev_notify_attributes; - char[56 - 3 * c_long.sizeof] __pad = void; - } + int sigqueue(pid_t, int, const sigval); + int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); + int sigwaitinfo(const scope sigset_t*, siginfo_t*); } else version (CRuntime_UClibc) { - private enum __SIGEV_MAX_SIZE = 64; - - static if ( __WORDSIZE == 64 ) - { - private enum __SIGEV_PAD_SIZE = ((__SIGEV_MAX_SIZE / int.sizeof) - 4); - } - else - { - private enum __SIGEV_PAD_SIZE = ((__SIGEV_MAX_SIZE / int.sizeof) - 3); - } - - struct sigevent - { - sigval sigev_value; - int sigev_signo; - int sigev_notify; - - union _sigev_un_t - { - int[__SIGEV_PAD_SIZE] _pad; - pid_t _tid; - - struct _sigev_thread_t - { - void function(sigval) _function; - void* _attribute; - } _sigev_thread_t _sigev_thread; - } _sigev_un_t _sigev_un; - } - - @property void function(sigval) sigev_notify_function(ref sigevent _sigevent) { return _sigevent._sigev_un._sigev_thread._function; } - @property void* sigev_notify_attributes(ref sigevent _sigevent) { return _sigevent._sigev_un._sigev_thread._attribute; } - int sigqueue(pid_t, int, const sigval); int sigtimedwait(const scope sigset_t*, siginfo_t*, const scope timespec*); int sigwaitinfo(const scope sigset_t*, siginfo_t*); diff --git a/libphobos/libdruntime/core/sys/posix/stdc/time.d b/libphobos/libdruntime/core/sys/posix/stdc/time.d index 89029de09a8..d48a0ea3eda 100644 --- a/libphobos/libdruntime/core/sys/posix/stdc/time.d +++ b/libphobos/libdruntime/core/sys/posix/stdc/time.d @@ -52,7 +52,27 @@ struct tm public import core.sys.posix.sys.types : time_t, clock_t; /// -version (OSX) +version (CRuntime_Glibc) +{ + enum clock_t CLOCKS_PER_SEC = 1_000_000; + clock_t clock(); +} +else version (CRuntime_Musl) +{ + enum clock_t CLOCKS_PER_SEC = 1_000_000; + clock_t clock(); +} +else version (CRuntime_Bionic) +{ + enum clock_t CLOCKS_PER_SEC = 1_000_000; + clock_t clock(); +} +else version (CRuntime_UClibc) +{ + enum clock_t CLOCKS_PER_SEC = 1_000_000; + clock_t clock(); +} +else version (OSX) { enum clock_t CLOCKS_PER_SEC = 1_000_000; // was 100 until OSX 10.4/10.5 version (X86) @@ -90,26 +110,6 @@ else version (Solaris) enum clock_t CLOCKS_PER_SEC = 1_000_000; clock_t clock(); } -else version (CRuntime_Glibc) -{ - enum clock_t CLOCKS_PER_SEC = 1_000_000; - clock_t clock(); -} -else version (CRuntime_Musl) -{ - enum clock_t CLOCKS_PER_SEC = 1_000_000; - clock_t clock(); -} -else version (CRuntime_Bionic) -{ - enum clock_t CLOCKS_PER_SEC = 1_000_000; - clock_t clock(); -} -else version (CRuntime_UClibc) -{ - enum clock_t CLOCKS_PER_SEC = 1_000_000; - clock_t clock(); -} else { static assert(0, "unsupported system"); diff --git a/libphobos/libdruntime/core/sys/posix/stdio.d b/libphobos/libdruntime/core/sys/posix/stdio.d index c8f92ec301b..077838d50aa 100644 --- a/libphobos/libdruntime/core/sys/posix/stdio.d +++ b/libphobos/libdruntime/core/sys/posix/stdio.d @@ -623,35 +623,35 @@ version (CRuntime_Glibc) { enum P_tmpdir = "/tmp"; } -version (CRuntime_Musl) +else version (CRuntime_Musl) { enum P_tmpdir = "/tmp"; } -version (Darwin) +else version (Darwin) { enum P_tmpdir = "/var/tmp"; } -version (FreeBSD) +else version (FreeBSD) { enum P_tmpdir = "/var/tmp/"; } -version (NetBSD) +else version (NetBSD) { enum P_tmpdir = "/var/tmp/"; } -version (OpenBSD) +else version (OpenBSD) { enum P_tmpdir = "/tmp/"; } -version (DragonFlyBSD) +else version (DragonFlyBSD) { enum P_tmpdir = "/var/tmp/"; } -version (Solaris) +else version (Solaris) { enum P_tmpdir = "/var/tmp/"; } -version (CRuntime_UClibc) +else version (CRuntime_UClibc) { enum P_tmpdir = "/tmp"; } diff --git a/libphobos/libdruntime/core/sys/posix/sys/ioctl.d b/libphobos/libdruntime/core/sys/posix/sys/ioctl.d index c6f21d6cd0a..36d1edc4ebc 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/ioctl.d +++ b/libphobos/libdruntime/core/sys/posix/sys/ioctl.d @@ -31,7 +31,7 @@ version (Posix): extern (C) nothrow @nogc: @system: -version (CRuntime_Glibc) +version (linux) { import core.sys.posix.termios; // tcflag_t, speed_t, cc_t @@ -326,8 +326,6 @@ version (CRuntime_Glibc) enum SIOCDEVPRIVATE = 0x89F0; enum SIOCPROTOPRIVATE = 0x89E0; - - int ioctl(int __fd, c_ulong __request, ...); } else version (Darwin) { @@ -348,8 +346,6 @@ else version (Darwin) enum uint TIOCSSIZE = TIOCSWINSZ; public import core.sys.posix.sys.filio; // File related ioctls - - int ioctl(int fildes, c_ulong request, ...); } else version (FreeBSD) { @@ -358,8 +354,6 @@ else version (FreeBSD) int len; void* buf; } - - int ioctl(int, c_ulong, ...); } else version (NetBSD) { @@ -370,8 +364,6 @@ else version (NetBSD) ushort ws_xpixel; ushort ws_ypixel; } - - int ioctl(int, c_ulong, ...); } else version (OpenBSD) { @@ -389,8 +381,6 @@ else version (OpenBSD) } public import core.sys.posix.sys.filio; // File related ioctls - - int ioctl(int, c_ulong, ...); } else version (DragonFlyBSD) { @@ -407,7 +397,38 @@ else version (DragonFlyBSD) ushort ws_xpixel; ushort ws_ypixel; } +} +else version (Solaris) +{ +} +else +{ + static assert(false, "Unsupported platform"); +} +/// +version (CRuntime_Glibc) +{ + int ioctl(int __fd, c_ulong __request, ...); +} +else version (Darwin) +{ + int ioctl(int fildes, c_ulong request, ...); +} +else version (FreeBSD) +{ + int ioctl(int, c_ulong, ...); +} +else version (NetBSD) +{ + int ioctl(int, c_ulong, ...); +} +else version (OpenBSD) +{ + int ioctl(int, c_ulong, ...); +} +else version (DragonFlyBSD) +{ int ioctl(int, c_ulong, ...); } else version (Solaris) @@ -420,331 +441,10 @@ else version (CRuntime_Bionic) } else version (CRuntime_Musl) { - + int ioctl(int, int, ...); } else version (CRuntime_UClibc) { - import core.sys.posix.termios; - - enum _IOC_NRBITS = 8; - enum _IOC_TYPEBITS = 8; - enum _IOC_SIZEBITS = 14; - enum _IOC_DIRBITS = 2; - - enum _IOC_NRMASK = (1 << _IOC_NRBITS) - 1; - enum _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1; - enum _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1; - enum _IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1; - - enum _IOC_NRSHIFT = 0; - enum _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS; - enum _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS; - enum _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS; - - enum _IOC_NONE = 0; - enum _IOC_WRITE = 1; - enum _IOC_READ = 2; - - extern (D) int _IOC(T = typeof(null))(int dir, int type, int nr) - { - return (dir << _IOC_DIRSHIFT) | - (type << _IOC_TYPESHIFT) | - (nr << _IOC_NRSHIFT) | - (is(T == typeof(null)) ? 0 : T.sizeof << _IOC_SIZESHIFT); - } - - extern (D) int _IO(int type, int nr) - { - return _IOC(_IOC_NONE, type, nr); - } - - extern (D) int _IOR(T)(int type, int nr) - { - return _IOC!T(_IOC_READ, type, nr); - } - - extern (D) int _IOW(T)(int type, int nr) - { - return _IOC!T(_IOC_WRITE, type, nr); - } - - extern (D) int _IOWR(T)(int type, int nr) - { - return _IOC!T(_IOC_READ | _IOC_WRITE, type, nr); - } - - extern (D) int _IOR_BAD(T)(int type, int nr) - { - return _IOC!T(_IOC_READ, type, nr); - } - - extern (D) int _IOW_BAD(T)(int type, int nr) - { - return _IOC!T(_IOC_WRITE, type, nr); - } - - extern (D) int _IORW_BAD(T)(int type, int nr) - { - return _IOC!T(_IOC_READ | _IOC_WRITE, type, nr); - } - - extern (D) int _IOC_DIR(int nr) - { - return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK; - } - - extern (D) int _IOC_TYPE(int nr) - { - return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK; - } - - extern (D) int _IOC_NR(int nr) - { - return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK; - } - - extern (D) int _IOC_SIZE(int nr) - { - return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK; - } - - enum IOC_IN = _IOC_WRITE << _IOC_DIRSHIFT; - enum IOC_OUT = _IOC_READ << _IOC_DIRSHIFT; - enum IOC_INOUT = (_IOC_READ | _IOC_WRITE) << _IOC_DIRSHIFT; - enum IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_DIRSHIFT; - enum IOCSIZE_SHIFT = _IOC_SIZESHIFT; - - enum NCCS = 19; - - struct termios - { - tcflag_t c_iflag; - tcflag_t c_oflag; - tcflag_t c_cflag; - tcflag_t c_lflag; - cc_t c_line; - cc_t[NCCS] c_cc; - } - - struct termios2 - { - tcflag_t c_iflag; - tcflag_t c_oflag; - tcflag_t c_cflag; - tcflag_t c_lflag; - cc_t c_line; - cc_t[NCCS] c_cc; - speed_t c_ispeed; - speed_t c_ospeed; - } - - alias termios2 ktermios; - - struct winsize - { - ushort ws_row; - ushort ws_col; - ushort ws_xpixel; - ushort ws_ypixel; - } - - enum NCC = 8; - - struct termio - { - ushort c_iflag; - ushort c_oflag; - ushort c_cflag; - ushort c_lflag; - ubyte c_line; - ubyte[NCC] c_cc; - } - - enum TIOCM_LE = 0x001; - enum TIOCM_DTR = 0x002; - enum TIOCM_RTS = 0x004; - enum TIOCM_ST = 0x008; - enum TIOCM_SR = 0x010; - enum TIOCM_CTS = 0x020; - enum TIOCM_CAR = 0x040; - enum TIOCM_RNG = 0x080; - enum TIOCM_DSR = 0x100; - enum TIOCM_CD = TIOCM_CAR; - enum TIOCM_RI = TIOCM_RNG; - - enum N_TTY = 0; - enum N_SLIP = 1; - enum N_MOUSE = 2; - enum N_PPP = 3; - enum N_STRIP = 4; - enum N_AX25 = 5; - enum N_X25 = 6; - enum N_6PACK = 7; - enum N_MASC = 8; - enum N_R3964 = 9; - enum N_PROFIBUS_FDL = 10; - enum N_IRDA = 11; - enum N_SMSBLOCK = 12; - enum N_HDLC = 13; - enum N_SYNC_PPP = 14; - enum N_HCI = 15; - - enum TCGETS = 0x5401; - enum TCSETS = 0x5402; - enum TCSETSW = 0x5403; - enum TCSETSF = 0x5404; - enum TCGETA = 0x5405; - enum TCSETA = 0x5406; - enum TCSETAW = 0x5407; - enum TCSETAF = 0x5408; - enum TCSBRK = 0x5409; - enum TCXONC = 0x540A; - enum TCFLSH = 0x540B; - enum TIOCEXCL = 0x540C; - enum TIOCNXCL = 0x540D; - enum TIOCSCTTY = 0x540E; - enum TIOCGPGRP = 0x540F; - enum TIOCSPGRP = 0x5410; - enum TIOCOUTQ = 0x5411; - enum TIOCSTI = 0x5412; - enum TIOCGWINSZ = 0x5413; - enum TIOCSWINSZ = 0x5414; - enum TIOCMGET = 0x5415; - enum TIOCMBIS = 0x5416; - enum TIOCMBIC = 0x5417; - enum TIOCMSET = 0x5418; - enum TIOCGSOFTCAR = 0x5419; - enum TIOCSSOFTCAR = 0x541A; - enum FIONREAD = 0x541B; - enum TIOCINQ = FIONREAD; - enum TIOCLINUX = 0x541C; - enum TIOCCONS = 0x541D; - enum TIOCGSERIAL = 0x541E; - enum TIOCSSERIAL = 0x541F; - enum TIOCPKT = 0x5420; - enum FIONBIO = 0x5421; - enum TIOCNOTTY = 0x5422; - enum TIOCSETD = 0x5423; - enum TIOCGETD = 0x5424; - enum TCSBRKP = 0x5425; - enum TIOCSBRK = 0x5427; - enum TIOCCBRK = 0x5428; - enum TIOCGSID = 0x5429; - - enum TCGETS2 = _IOR!termios2('T', 0x2A); - enum TCSETS2 = _IOW!termios2('T', 0x2B); - enum TCSETSW2 = _IOW!termios2('T', 0x2C); - enum TCSETSF2 = _IOW!termios2('T', 0x2D); - - enum TIOCGRS485 = 0x542E; - enum TIOCSRS485 = 0x542F; - - enum TIOCGPTN = _IOR!uint('T', 0x30); - enum TIOCSPTLCK = _IOW!int('T', 0x31); - enum TIOCGDEV = _IOR!uint('T', 0x32); - - enum TCGETX = 0x5432; - enum TCSETX = 0x5433; - enum TCSETXF = 0x5434; - enum TCSETXW = 0x5435; - - enum TIOCSIG = _IOW!int('T', 0x36); - - enum TIOCVHANGUP = 0x5437; - - enum FIONCLEX = 0x5450; - enum FIOCLEX = 0x5451; - enum FIOASYNC = 0x5452; - enum TIOCSERCONFIG = 0x5453; - enum TIOCSERGWILD = 0x5454; - enum TIOCSERSWILD = 0x5455; - enum TIOCGLCKTRMIOS = 0x5456; - enum TIOCSLCKTRMIOS = 0x5457; - enum TIOCSERGSTRUCT = 0x5458; - enum TIOCSERGETLSR = 0x5459; - enum TIOCSERGETMULTI = 0x545A; - enum TIOCSERSETMULTI = 0x545B; - - enum TIOCMIWAIT = 0x545C; - enum TIOCGICOUNT = 0x545D; - - enum FIOQSIZE = 0x5460; - - enum TIOCPKT_DATA = 0; - enum TIOCPKT_FLUSHREAD = 1; - enum TIOCPKT_FLUSHWRITE = 2; - enum TIOCPKT_STOP = 4; - enum TIOCPKT_START = 8; - enum TIOCPKT_NOSTOP = 16; - enum TIOCPKT_DOSTOP = 32; - enum TIOCPKT_IOCTL = 64; - - enum TIOCSER_TEMT = 0x01; - - enum SIOCADDRT = 0x890B; - enum SIOCDELRT = 0x890C; - enum SIOCRTMSG = 0x890D; - - enum SIOCGIFNAME = 0x8910; - enum SIOCSIFLINK = 0x8911; - enum SIOCGIFCONF = 0x8912; - enum SIOCGIFFLAGS = 0x8913; - enum SIOCSIFFLAGS = 0x8914; - enum SIOCGIFADDR = 0x8915; - enum SIOCSIFADDR = 0x8916; - enum SIOCGIFDSTADDR = 0x8917; - enum SIOCSIFDSTADDR = 0x8918; - enum SIOCGIFBRDADDR = 0x8919; - enum SIOCSIFBRDADDR = 0x891a; - enum SIOCGIFNETMASK = 0x891b; - enum SIOCSIFNETMASK = 0x891c; - enum SIOCGIFMETRIC = 0x891d; - enum SIOCSIFMETRIC = 0x891e; - enum SIOCGIFMEM = 0x891f; - enum SIOCSIFMEM = 0x8920; - enum SIOCGIFMTU = 0x8921; - enum SIOCSIFMTU = 0x8922; - enum SIOCSIFNAME = 0x8923; - enum SIOCSIFHWADDR = 0x8924; - enum SIOCGIFENCAP = 0x8925; - enum SIOCSIFENCAP = 0x8926; - enum SIOCGIFHWADDR = 0x8927; - enum SIOCGIFSLAVE = 0x8929; - enum SIOCSIFSLAVE = 0x8930; - enum SIOCADDMULTI = 0x8931; - enum SIOCDELMULTI = 0x8932; - enum SIOCGIFINDEX = 0x8933; - enum SIOGIFINDEX = SIOCGIFINDEX; - enum SIOCSIFPFLAGS = 0x8934; - enum SIOCGIFPFLAGS = 0x8935; - enum SIOCDIFADDR = 0x8936; - enum SIOCSIFHWBROADCAST = 0x8937; - enum SIOCGIFCOUNT = 0x8938; - - enum SIOCGIFBR = 0x8940; - enum SIOCSIFBR = 0x8941; - - enum SIOCGIFTXQLEN = 0x8942; - enum SIOCSIFTXQLEN = 0x8943; - - enum SIOCDARP = 0x8953; - enum SIOCGARP = 0x8954; - enum SIOCSARP = 0x8955; - - enum SIOCDRARP = 0x8960; - enum SIOCGRARP = 0x8961; - enum SIOCSRARP = 0x8962; - - enum SIOCGIFMAP = 0x8970; - enum SIOCSIFMAP = 0x8971; - - enum SIOCADDDLCI = 0x8980; - enum SIOCDELDLCI = 0x8981; - - enum SIOCDEVPRIVATE = 0x89F0; - - enum SIOCPROTOPRIVATE = 0x89E0; - int ioctl(int __fd, c_ulong __request, ...); } else diff --git a/libphobos/libdruntime/core/sys/posix/sys/ipc.d b/libphobos/libdruntime/core/sys/posix/sys/ipc.d index d397a28ec5a..18a6cbd4d53 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/ipc.d +++ b/libphobos/libdruntime/core/sys/posix/sys/ipc.d @@ -52,11 +52,9 @@ IPC_PRIVATE IPC_RMID IPC_SET IPC_STAT - -key_t ftok(const scope char*, int); */ -version (CRuntime_Glibc) +version (linux) { struct ipc_perm { @@ -82,8 +80,6 @@ version (CRuntime_Glibc) enum IPC_RMID = 0; enum IPC_SET = 1; enum IPC_STAT = 2; - - key_t ftok(const scope char*, int); } else version (Darwin) { @@ -122,8 +118,6 @@ else version (FreeBSD) enum IPC_RMID = 0; enum IPC_SET = 1; enum IPC_STAT = 2; - - key_t ftok(const scope char*, int); } else version (NetBSD) { @@ -147,8 +141,6 @@ else version (NetBSD) enum IPC_RMID = 0; enum IPC_SET = 1; enum IPC_STAT = 2; - - key_t ftok(const scope char*, int); } else version (OpenBSD) { @@ -172,8 +164,6 @@ else version (OpenBSD) enum IPC_RMID = 0; enum IPC_SET = 1; enum IPC_STAT = 2; - - key_t ftok(const scope char*, int); } else version (DragonFlyBSD) { @@ -197,79 +187,53 @@ else version (DragonFlyBSD) enum IPC_RMID = 0; enum IPC_SET = 1; enum IPC_STAT = 2; - - key_t ftok(const scope char*, int); } -else version (CRuntime_Bionic) +else { - // All except ftok are from the linux kernel headers. Latest Bionic headers - // don't use this legacy definition anymore, consider updating. - version (D_LP64) - { - struct ipc_perm - { - key_t key; - uint uid; - uint gid; - uint cuid; - uint cgid; - mode_t mode; - ushort seq; - } - } - else - { - struct ipc_perm - { - key_t key; - ushort uid; - ushort gid; - ushort cuid; - ushort cgid; - mode_t mode; - ushort seq; - } - } - - enum IPC_CREAT = 0x0200; // 01000 - enum IPC_EXCL = 0x0400; // 02000 - enum IPC_NOWAIT = 0x0800; // 04000 + static assert(false, "Unsupported platform"); +} - enum key_t IPC_PRIVATE = 0; +/* +key_t ftok(const scope char*, int); +*/ - enum IPC_RMID = 0; - enum IPC_SET = 1; - enum IPC_STAT = 2; +version (CRuntime_Glibc) +{ + key_t ftok(const scope char*, int); +} +else version (Darwin) +{ +} +else version (FreeBSD) +{ + key_t ftok(const scope char*, int); +} +else version (NetBSD) +{ + key_t ftok(const scope char*, int); +} +else version (OpenBSD) +{ + key_t ftok(const scope char*, int); +} +else version (DragonFlyBSD) +{ + key_t ftok(const scope char*, int); +} +else version (CRuntime_Bionic) +{ + key_t ftok(const scope char*, int); +} +else version (CRuntime_Musl) +{ key_t ftok(const scope char*, int); } else version (CRuntime_UClibc) { - struct ipc_perm - { - key_t __key; - uid_t uid; - gid_t gid; - uid_t cuid; - gid_t cgid; - ushort mode; - ushort __pad1; - ushort __seq; - ushort __pad2; - c_ulong __unused1; - c_ulong __unused2; - } - - enum IPC_CREAT = 0x0200; // 01000 - enum IPC_EXCL = 0x0400; // 02000 - enum IPC_NOWAIT = 0x0800; // 04000 - - enum key_t IPC_PRIVATE = 0; - - enum IPC_RMID = 0; - enum IPC_SET = 1; - enum IPC_STAT = 2; - enum IPC_INFO = 3; - key_t ftok(const scope char*, int); } +else +{ + static assert(false, "Unsupported platform"); +} diff --git a/libphobos/libdruntime/core/sys/posix/sys/mman.d b/libphobos/libdruntime/core/sys/posix/sys/mman.d index 18da10246e4..33ce88feb4e 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/mman.d +++ b/libphobos/libdruntime/core/sys/posix/sys/mman.d @@ -54,6 +54,53 @@ extern (C) nothrow @nogc: int posix_madvise(void*, size_t, int); */ +version (CRuntime_Glibc) +{ + static if (__USE_XOPEN2K) + { + int posix_madvise(void *__addr, size_t __len, int __advice); + } +} +else version (Darwin) +{ + int posix_madvise(void *addr, size_t len, int advice); +} +else version (FreeBSD) +{ + int posix_madvise(void *addr, size_t len, int advice); +} +else version (NetBSD) +{ + int posix_madvise(void *addr, size_t len, int advice); +} +else version (OpenBSD) +{ + int posix_madvise(void *addr, size_t len, int advice); +} +else version (DragonFlyBSD) +{ + int posix_madvise(void *addr, size_t len, int advice); +} +else version (Solaris) +{ +} +else version (CRuntime_Bionic) +{ +} +else version (CRuntime_Musl) +{ + int posix_madvise(void *, size_t, int); +} +else version (CRuntime_UClibc) +{ + int posix_madvise(void *__addr, size_t __len, int __advice); +} +else +{ + static assert(false, "Unsupported platform"); +} + + // // Advisory Information and either Memory Mapped Files or Shared Memory Objects (MC1) // @@ -65,24 +112,20 @@ POSIX_MADV_WILLNEED POSIX_MADV_DONTNEED */ -version (CRuntime_Glibc) +version (linux) { version (Alpha) private enum __POSIX_MADV_DONTNEED = 6; else private enum __POSIX_MADV_DONTNEED = 4; - static if (__USE_XOPEN2K) + enum { - enum - { - POSIX_MADV_NORMAL = 0, - POSIX_MADV_RANDOM = 1, - POSIX_MADV_SEQUENTIAL = 2, - POSIX_MADV_WILLNEED = 3, - POSIX_MADV_DONTNEED = __POSIX_MADV_DONTNEED, - } - int posix_madvise(void *__addr, size_t __len, int __advice); + POSIX_MADV_NORMAL = 0, + POSIX_MADV_RANDOM = 1, + POSIX_MADV_SEQUENTIAL = 2, + POSIX_MADV_WILLNEED = 3, + POSIX_MADV_DONTNEED = __POSIX_MADV_DONTNEED, } } else version (Darwin) @@ -92,7 +135,6 @@ else version (Darwin) enum POSIX_MADV_SEQUENTIAL = 2; enum POSIX_MADV_WILLNEED = 3; enum POSIX_MADV_DONTNEED = 4; - int posix_madvise(void *addr, size_t len, int advice); } else version (FreeBSD) { @@ -101,7 +143,6 @@ else version (FreeBSD) enum POSIX_MADV_SEQUENTIAL = 2; enum POSIX_MADV_WILLNEED = 3; enum POSIX_MADV_DONTNEED = 4; - int posix_madvise(void *addr, size_t len, int advice); } else version (NetBSD) { @@ -110,7 +151,6 @@ else version (NetBSD) enum POSIX_MADV_SEQUENTIAL = 2; enum POSIX_MADV_WILLNEED = 3; enum POSIX_MADV_DONTNEED = 4; - int posix_madvise(void *addr, size_t len, int advice); } else version (OpenBSD) { @@ -119,7 +159,6 @@ else version (OpenBSD) enum POSIX_MADV_SEQUENTIAL = 2; enum POSIX_MADV_WILLNEED = 3; enum POSIX_MADV_DONTNEED = 4; - int posix_madvise(void *addr, size_t len, int advice); } else version (DragonFlyBSD) { @@ -128,38 +167,10 @@ else version (DragonFlyBSD) enum POSIX_MADV_SEQUENTIAL = 2; enum POSIX_MADV_WILLNEED = 3; enum POSIX_MADV_DONTNEED = 4; - int posix_madvise(void *addr, size_t len, int advice); } else version (Solaris) { } -else version (CRuntime_Bionic) -{ -} -else version (CRuntime_Musl) -{ - enum - { - POSIX_MADV_NORMAL = 0, - POSIX_MADV_RANDOM = 1, - POSIX_MADV_SEQUENTIAL = 2, - POSIX_MADV_WILLNEED = 3, - POSIX_MADV_DONTNEED = 4, - } - int posix_madvise(void *, size_t, int); -} -else version (CRuntime_UClibc) -{ - enum - { - POSIX_MADV_NORMAL = 0, - POSIX_MADV_RANDOM = 1, - POSIX_MADV_SEQUENTIAL = 2, - POSIX_MADV_WILLNEED = 3, - POSIX_MADV_DONTNEED = 4, - } - int posix_madvise(void *__addr, size_t __len, int __advice); -} else { static assert(false, "Unsupported platform"); @@ -175,7 +186,7 @@ PROT_EXEC PROT_NONE */ -version (CRuntime_Glibc) +version (linux) { enum PROT_NONE = 0x0; enum PROT_READ = 0x1; @@ -224,27 +235,6 @@ else version (Solaris) enum PROT_WRITE = 0x02; enum PROT_EXEC = 0x04; } -else version (CRuntime_Bionic) -{ - enum PROT_NONE = 0x00; - enum PROT_READ = 0x01; - enum PROT_WRITE = 0x02; - enum PROT_EXEC = 0x04; -} -else version (CRuntime_Musl) -{ - enum PROT_NONE = 0x0; - enum PROT_READ = 0x1; - enum PROT_WRITE = 0x2; - enum PROT_EXEC = 0x4; -} -else version (CRuntime_UClibc) -{ - enum PROT_NONE = 0x0; - enum PROT_READ = 0x1; - enum PROT_WRITE = 0x2; - enum PROT_EXEC = 0x4; -} else { static assert(false, "Unsupported platform"); @@ -337,11 +327,9 @@ MAP_FAILED (MF|SHM) MS_ASYNC (MF|SIO) MS_SYNC (MF|SIO) MS_INVALIDATE (MF|SIO) - -int msync(void*, size_t, int); (MF|SIO) */ -version (CRuntime_Glibc) +version (linux) { enum MAP_SHARED = 0x01; enum MAP_PRIVATE = 0x02; @@ -405,8 +393,6 @@ version (CRuntime_Glibc) enum MS_INVALIDATE = 2; enum MS_SYNC = 4; } - - int msync(void*, size_t, int); } else version (Darwin) { @@ -420,8 +406,6 @@ else version (Darwin) enum MS_ASYNC = 0x0001; enum MS_INVALIDATE = 0x0002; enum MS_SYNC = 0x0010; - - int msync(void*, size_t, int); } else version (FreeBSD) { @@ -435,8 +419,6 @@ else version (FreeBSD) enum MS_SYNC = 0x0000; enum MS_ASYNC = 0x0001; enum MS_INVALIDATE = 0x0002; - - int msync(void*, size_t, int); } else version (NetBSD) { @@ -450,9 +432,6 @@ else version (NetBSD) enum MS_SYNC = 0x0004; enum MS_ASYNC = 0x0001; enum MS_INVALIDATE = 0x0002; - - int __msync13(void*, size_t, int); - alias msync = __msync13; } else version (OpenBSD) { @@ -467,8 +446,6 @@ else version (OpenBSD) enum MS_SYNC = 0x0002; enum MS_ASYNC = 0x0001; enum MS_INVALIDATE = 0x0004; - - int msync(void*, size_t, int); } else version (DragonFlyBSD) { @@ -482,8 +459,6 @@ else version (DragonFlyBSD) enum MS_SYNC = 0x0000; enum MS_ASYNC = 0x0001; enum MS_INVALIDATE = 0x0002; - - int msync(void*, size_t, int); } else version (Solaris) { @@ -497,72 +472,55 @@ else version (Solaris) enum MS_SYNC = 0x0004; enum MS_ASYNC = 0x0001; enum MS_INVALIDATE = 0x0002; +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +int msync(void*, size_t, int); (MF|SIO) +*/ +version (CRuntime_Glibc) +{ + int msync(void*, size_t, int); +} +else version (Darwin) +{ + int msync(void*, size_t, int); +} +else version (FreeBSD) +{ + int msync(void*, size_t, int); +} +else version (NetBSD) +{ + int __msync13(void*, size_t, int); + alias msync = __msync13; +} +else version (OpenBSD) +{ + int msync(void*, size_t, int); +} +else version (DragonFlyBSD) +{ + int msync(void*, size_t, int); +} +else version (Solaris) +{ int msync(void*, size_t, int); } else version (CRuntime_Bionic) { - enum MAP_SHARED = 0x0001; - enum MAP_PRIVATE = 0x0002; - enum MAP_FIXED = 0x0010; - enum MAP_ANON = 0x0020; - - enum MAP_FAILED = cast(void*)-1; - - enum MS_SYNC = 4; - enum MS_ASYNC = 1; - enum MS_INVALIDATE = 2; - int msync(const scope void*, size_t, int); } else version (CRuntime_Musl) { - enum MAP_SHARED = 0x01; - enum MAP_PRIVATE = 0x02; - enum MAP_FIXED = 0x10; - - enum MAP_FAILED = cast(void*) -1; - enum MAP_ANON = 0x20; - enum MS_ASYNC = 1; - enum MS_INVALIDATE = 2; - enum MS_SYNC = 4; int msync(void*, size_t, int); } else version (CRuntime_UClibc) { - enum MAP_SHARED = 0x01; - enum MAP_PRIVATE = 0x02; - enum MAP_FIXED = 0x10; - - enum MAP_FAILED = cast(void*) -1; - - version (X86_64) - { - enum MAP_ANON = 0x20; - enum MS_ASYNC = 1; - enum MS_INVALIDATE = 2; - enum MS_SYNC = 4; - } - else version (MIPS32) - { - enum MAP_ANON = 0x0800; - enum MS_ASYNC = 1; - enum MS_INVALIDATE = 2; - enum MS_SYNC = 4; - } - else version (ARM) - { - enum MAP_ANON = 0x020; - enum MS_ASYNC = 1; - enum MS_INVALIDATE = 2; - enum MS_SYNC = 4; - } - else - { - static assert(false, "Architecture not supported."); - } - - int msync(void*, size_t, int); } else @@ -576,12 +534,9 @@ else /* MCL_CURRENT MCL_FUTURE - -int mlockall(int); -int munlockall(); */ -version (CRuntime_Glibc) +version (linux) { version (SPARC_Any) enum { @@ -603,89 +558,96 @@ version (CRuntime_Glibc) MCL_CURRENT = 1, MCL_FUTURE = 2, } - - int mlockall(int); - int munlockall(); - } else version (Darwin) { enum MCL_CURRENT = 0x0001; enum MCL_FUTURE = 0x0002; - - int mlockall(int); - int munlockall(); } else version (FreeBSD) { enum MCL_CURRENT = 0x0001; enum MCL_FUTURE = 0x0002; - - int mlockall(int); - int munlockall(); } else version (NetBSD) { enum MCL_CURRENT = 0x0001; enum MCL_FUTURE = 0x0002; - - int mlockall(int); - int munlockall(); } else version (OpenBSD) { enum MCL_CURRENT = 0x0001; enum MCL_FUTURE = 0x0002; - - int mlockall(int); - int munlockall(); } else version (DragonFlyBSD) { enum MCL_CURRENT = 0x0001; enum MCL_FUTURE = 0x0002; - - int mlockall(int); - int munlockall(); } else version (Solaris) { enum MCL_CURRENT = 0x0001; enum MCL_FUTURE = 0x0002; +} +else +{ + static assert(false, "Unsupported platform"); +} +/* +int mlockall(int); +int munlockall(); +*/ + +version (CRuntime_Glibc) +{ + int mlockall(int); + int munlockall(); +} +else version (Darwin) +{ + int mlockall(int); + int munlockall(); +} +else version (FreeBSD) +{ + int mlockall(int); + int munlockall(); +} +else version (NetBSD) +{ + int mlockall(int); + int munlockall(); +} +else version (OpenBSD) +{ + int mlockall(int); + int munlockall(); +} +else version (DragonFlyBSD) +{ + int mlockall(int); + int munlockall(); +} +else version (Solaris) +{ int mlockall(int); int munlockall(); } else version (CRuntime_Bionic) { - enum MCL_CURRENT = 1; - enum MCL_FUTURE = 2; - int mlockall(int); int munlockall(); } else version (CRuntime_Musl) { - enum - { - MCL_CURRENT = 1, - MCL_FUTURE = 2, - } - int mlockall(int); int munlockall(); } else version (CRuntime_UClibc) { - enum - { - MCL_CURRENT = 1, - MCL_FUTURE = 2, - } - int mlockall(int); int munlockall(); - } else { diff --git a/libphobos/libdruntime/core/sys/posix/sys/resource.d b/libphobos/libdruntime/core/sys/posix/sys/resource.d index c5d584c5804..5ab01744067 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/resource.d +++ b/libphobos/libdruntime/core/sys/posix/sys/resource.d @@ -52,12 +52,6 @@ enum RUSAGE_CHILDREN, } -struct rlimit -{ - rlim_t rlim_cur; - rlim_t rlim_max; -} - struct rusage { timeval ru_utime; @@ -74,19 +68,10 @@ enum RLIMIT_STACK, RLIMIT_AS, } - -int getpriority(int, id_t); -int getrlimit(int, rlimit*); -int getrusage(int, rusage*); -int setpriority(int, id_t, int); -int setrlimit(int, const rlimit*); */ - -version (CRuntime_Glibc) +version (linux) { - // rusage and some other constants in the Bionic section below really - // come from the linux kernel headers, but they're all mixed right now. enum { PRIO_PROCESS = 0, @@ -111,6 +96,7 @@ version (CRuntime_Glibc) { RUSAGE_SELF = 0, RUSAGE_CHILDREN = -1, + RUSAGE_THREAD = 1 } struct rusage @@ -131,6 +117,8 @@ version (CRuntime_Glibc) c_long ru_nsignals; c_long ru_nvcsw; c_long ru_nivcsw; + version (CRuntime_Musl) + c_long[16] __reserved; } enum @@ -200,9 +188,8 @@ else version (FreeBSD) enum { RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), - // FreeBSD explicitly does not define the following: - //RLIM_SAVED_MAX, - //RLIM_SAVED_CUR, + RLIM_SAVED_MAX = RLIM_INFINITY, + RLIM_SAVED_CUR = RLIM_INFINITY, } enum @@ -258,9 +245,8 @@ else version (NetBSD) enum { RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), - // FreeBSD explicitly does not define the following: - //RLIM_SAVED_MAX, - //RLIM_SAVED_CUR, + RLIM_SAVED_MAX = RLIM_INFINITY, + RLIM_SAVED_CUR = RLIM_INFINITY, } enum @@ -375,9 +361,8 @@ else version (DragonFlyBSD) enum { RLIM_INFINITY = (cast(rlim_t)((cast(ulong) 1 << 63) - 1)), - // DragonFlyBSD explicitly does not define the following: - //RLIM_SAVED_MAX, - //RLIM_SAVED_CUR, + RLIM_SAVED_MAX = RLIM_INFINITY, + RLIM_SAVED_CUR = RLIM_INFINITY, } enum @@ -474,162 +459,22 @@ else version (Solaris) RLIMIT_AS = 6, } } -else version (CRuntime_Bionic) -{ - enum - { - PRIO_PROCESS = 0, - PRIO_PGRP = 1, - PRIO_USER = 2, - } - - alias c_ulong rlim_t; - enum RLIM_INFINITY = cast(c_ulong)(~0UL); - - enum - { - RUSAGE_SELF = 0, - RUSAGE_CHILDREN = -1, - } +else + static assert (false, "Unsupported platform"); - struct rusage - { - timeval ru_utime; - timeval ru_stime; - c_long ru_maxrss; - c_long ru_ixrss; - c_long ru_idrss; - c_long ru_isrss; - c_long ru_minflt; - c_long ru_majflt; - c_long ru_nswap; - c_long ru_inblock; - c_long ru_oublock; - c_long ru_msgsnd; - c_long ru_msgrcv; - c_long ru_nsignals; - c_long ru_nvcsw; - c_long ru_nivcsw; - } - - enum - { - RLIMIT_CORE = 4, - RLIMIT_CPU = 0, - RLIMIT_DATA = 2, - RLIMIT_FSIZE = 1, - RLIMIT_NOFILE = 7, - RLIMIT_STACK = 3, - RLIMIT_AS = 9, - } -} -else version (CRuntime_Musl) +/* +struct rlimit { - alias ulong rlim_t; - enum RLIM_INFINITY = cast(c_ulong)(~0UL); - - int getrlimit(int, rlimit*); - int setrlimit(int, const scope rlimit*); - alias getrlimit getrlimit64; - alias setrlimit setrlimit64; - enum - { - RUSAGE_SELF = 0, - RUSAGE_CHILDREN = -1, - RUSAGE_THREAD = 1 - } - struct rusage - { - timeval ru_utime; - timeval ru_stime; - c_long ru_maxrss; - c_long ru_ixrss; - c_long ru_idrss; - c_long ru_isrss; - c_long ru_minflt; - c_long ru_majflt; - c_long ru_nswap; - c_long ru_inblock; - c_long ru_oublock; - c_long ru_msgsnd; - c_long ru_msgrcv; - c_long ru_nsignals; - c_long ru_nvcsw; - c_long ru_nivcsw; - c_long[16] __reserved; - } - - enum - { - RLIMIT_CPU = 0, - RLIMIT_FSIZE = 1, - RLIMIT_DATA = 2, - RLIMIT_STACK = 3, - RLIMIT_CORE = 4, - RLIMIT_NOFILE = 7, - RLIMIT_AS = 9, - } + rlim_t rlim_cur; + rlim_t rlim_max; } -else version (CRuntime_UClibc) -{ - enum - { - PRIO_PROCESS = 0, - PRIO_PGRP = 1, - PRIO_USER = 2, - } - - static if (__USE_FILE_OFFSET64) - alias ulong rlim_t; - else - alias c_ulong rlim_t; - - static if (__USE_FILE_OFFSET64) - enum RLIM_INFINITY = 0xffffffffffffffffUL; - else - enum RLIM_INFINITY = cast(c_ulong)(~0UL); - - enum RLIM_SAVED_MAX = RLIM_INFINITY; - enum RLIM_SAVED_CUR = RLIM_INFINITY; - - enum - { - RUSAGE_SELF = 0, - RUSAGE_CHILDREN = -1, - } - - struct rusage - { - timeval ru_utime; - timeval ru_stime; - c_long ru_maxrss; - c_long ru_ixrss; - c_long ru_idrss; - c_long ru_isrss; - c_long ru_minflt; - c_long ru_majflt; - c_long ru_nswap; - c_long ru_inblock; - c_long ru_oublock; - c_long ru_msgsnd; - c_long ru_msgrcv; - c_long ru_nsignals; - c_long ru_nvcsw; - c_long ru_nivcsw; - } - enum - { - RLIMIT_CORE = 4, - RLIMIT_CPU = 0, - RLIMIT_DATA = 2, - RLIMIT_FSIZE = 1, - RLIMIT_NOFILE = 7, - RLIMIT_STACK = 3, - RLIMIT_AS = 9, - } -} -else static assert (false, "Unsupported platform"); +int getpriority(int, id_t); +int getrlimit(int, rlimit*); +int getrusage(int, rusage*); +int setpriority(int, id_t, int); +int setrlimit(int, const rlimit*); +*/ struct rlimit { @@ -641,40 +486,6 @@ version (CRuntime_Glibc) { int getpriority(int, id_t); int setpriority(int, id_t, int); -} -else version (FreeBSD) -{ - int getpriority(int, int); - int setpriority(int, int, int); -} -else version (DragonFlyBSD) -{ - int getpriority(int, int); - int setpriority(int, int, int); -} -else version (CRuntime_Bionic) -{ - int getpriority(int, int); - int setpriority(int, int, int); -} -else version (Solaris) -{ - int getpriority(int, id_t); - int setpriority(int, id_t, int); -} -else version (Darwin) -{ - int getpriority(int, id_t); - int setpriority(int, id_t, int); -} -else version (CRuntime_UClibc) -{ - int getpriority(int, id_t); - int setpriority(int, id_t, int); -} - -version (CRuntime_Glibc) -{ static if (__USE_FILE_OFFSET64) { int getrlimit64(int, rlimit*); @@ -689,50 +500,76 @@ version (CRuntime_Glibc) } int getrusage(int, rusage*); } -else version (CRuntime_Bionic) +else version (FreeBSD) { + int getpriority(int, int); int getrlimit(int, rlimit*); int getrusage(int, rusage*); + int setpriority(int, int, int); int setrlimit(int, const scope rlimit*); } -else version (Darwin) +else version (NetBSD) { + int getpriority(int, int); int getrlimit(int, rlimit*); int getrusage(int, rusage*); + int setpriority(int, int, int); int setrlimit(int, const scope rlimit*); } -else version (FreeBSD) +else version (OpenBSD) { + int getpriority(int, int); int getrlimit(int, rlimit*); int getrusage(int, rusage*); + int setpriority(int, int, int); int setrlimit(int, const scope rlimit*); } -else version (NetBSD) +else version (DragonFlyBSD) { + int getpriority(int, int); int getrlimit(int, rlimit*); int getrusage(int, rusage*); + int setpriority(int, int, int); int setrlimit(int, const scope rlimit*); } -else version (OpenBSD) +else version (CRuntime_Bionic) { + int getpriority(int, int); int getrlimit(int, rlimit*); int getrusage(int, rusage*); + int setpriority(int, int, int); int setrlimit(int, const scope rlimit*); } -else version (DragonFlyBSD) +else version (CRuntime_Musl) { + int getpriority(int, id_t); + int setpriority(int, id_t, int); int getrlimit(int, rlimit*); - int getrusage(int, rusage*); int setrlimit(int, const scope rlimit*); + alias getrlimit getrlimit64; + alias setrlimit setrlimit64; + int getrusage(int, rusage*); } else version (Solaris) { + int getpriority(int, int); int getrlimit(int, rlimit*); int getrusage(int, rusage*); + int setpriority(int, int, int); + int setrlimit(int, const scope rlimit*); +} +else version (Darwin) +{ + int getpriority(int, id_t); + int getrlimit(int, rlimit*); + int getrusage(int, rusage*); + int setpriority(int, id_t, int); int setrlimit(int, const scope rlimit*); } else version (CRuntime_UClibc) { + int getpriority(int, id_t); + int setpriority(int, id_t, int); static if (__USE_FILE_OFFSET64) { int getrlimit64(int, rlimit*); @@ -747,3 +584,5 @@ else version (CRuntime_UClibc) } int getrusage(int, rusage*); } +else + static assert (false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/shm.d b/libphobos/libdruntime/core/sys/posix/sys/shm.d index 2e85096ba4a..ce341418f36 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/shm.d +++ b/libphobos/libdruntime/core/sys/posix/sys/shm.d @@ -38,8 +38,6 @@ extern (C) nothrow @nogc: SHM_RDONLY SHM_RND -SHMLBA - shmatt_t struct shmid_ds @@ -53,20 +51,13 @@ struct shmid_ds time_t shm_dtime; time_t shm_ctime; } - -void* shmat(int, const scope void*, int); -int shmctl(int, int, shmid_ds*); -int shmdt(const scope void*); -int shmget(key_t, size_t, int); */ -version (CRuntime_Glibc) +version (linux) { enum SHM_RDONLY = 0x01000; // 010000 enum SHM_RND = 0x02000; // 020000 - - int __getpagesize(); - alias __getpagesize SHMLBA; + enum SHM_REMAP = 0x4000; // 040000 alias c_ulong shmatt_t; @@ -87,17 +78,11 @@ version (CRuntime_Glibc) c_ulong __unused4; c_ulong __unused5; } - - void* shmat(int, const scope void*, int); - int shmctl(int, int, shmid_ds*); - int shmdt(const scope void*); - int shmget(key_t, size_t, int); } else version (FreeBSD) { enum SHM_RDONLY = 0x01000; // 010000 enum SHM_RND = 0x02000; // 020000 - enum SHMLBA = 1 << 12; // PAGE_SIZE = (1<<PAGE_SHIFT) alias c_ulong shmatt_t; @@ -125,17 +110,11 @@ else version (FreeBSD) time_t shm_dtime; time_t shm_ctime; } - - void* shmat(int, const scope void*, int); - int shmctl(int, int, shmid_ds*); - int shmdt(const scope void*); - int shmget(key_t, size_t, int); } else version (NetBSD) { enum SHM_RDONLY = 0x01000; // 010000 enum SHM_RND = 0x02000; // 020000 - enum SHMLBA = 1 << 12; // PAGE_SIZE = (1<<PAGE_SHIFT) alias c_ulong shmatt_t; @@ -151,17 +130,11 @@ else version (NetBSD) time_t shm_ctime; void* shm_internal; } - - void* shmat(int, const scope void*, int); - int shmctl(int, int, shmid_ds*); - int shmdt(const scope void*); - int shmget(key_t, size_t, int); } else version (OpenBSD) { enum SHM_RDONLY = 0x01000; // 010000 enum SHM_RND = 0x02000; // 020000 - enum SHMLBA = 1 << _MAX_PAGE_SHIFT; alias short shmatt_t; @@ -180,17 +153,11 @@ else version (OpenBSD) c_long __shm_ctimensec; void* shm_internal; } - - void* shmat(int, const scope void*, int); - int shmctl(int, int, shmid_ds*); - int shmdt(const scope void*); - int shmget(key_t, size_t, int); } else version (DragonFlyBSD) { enum SHM_RDONLY = 0x01000; // 010000 enum SHM_RND = 0x02000; // 020000 - enum SHMLBA = 1 << 12; // PAGE_SIZE = (1<<PAGE_SHIFT) alias c_ulong shmatt_t; @@ -206,76 +173,103 @@ else version (DragonFlyBSD) time_t shm_ctime; private void* shm_internal; } +} +else version (Darwin) +{ + +} +else version (Solaris) +{ + +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +SHMLBA + +void* shmat(int, const scope void*, int); +int shmctl(int, int, shmid_ds*); +int shmdt(const scope void*); +int shmget(key_t, size_t, int); +*/ + +version (CRuntime_Glibc) +{ + int __getpagesize(); + alias __getpagesize SHMLBA; void* shmat(int, const scope void*, int); int shmctl(int, int, shmid_ds*); int shmdt(const scope void*); int shmget(key_t, size_t, int); } -else version (Darwin) +else version (FreeBSD) { + enum SHMLBA = 1 << 12; // PAGE_SIZE = (1<<PAGE_SHIFT) + void* shmat(int, const scope void*, int); + int shmctl(int, int, shmid_ds*); + int shmdt(const scope void*); + int shmget(key_t, size_t, int); } -else version (CRuntime_UClibc) +else version (NetBSD) { - enum SHM_RDONLY = 0x1000; // 010000 - enum SHM_RND = 0x2000; // 020000 - enum SHM_REMAP = 0x4000; // 040000 + enum SHMLBA = 1 << 12; // PAGE_SIZE = (1<<PAGE_SHIFT) - int __getpagesize(); - alias __getpagesize SHMLBA; + void* shmat(int, const scope void*, int); + int shmctl(int, int, shmid_ds*); + int shmdt(const scope void*); + int shmget(key_t, size_t, int); +} +else version (OpenBSD) +{ + enum SHMLBA = 1 << _MAX_PAGE_SHIFT; - alias c_ulong shmatt_t; + void* shmat(int, const scope void*, int); + int shmctl(int, int, shmid_ds*); + int shmdt(const scope void*); + int shmget(key_t, size_t, int); +} +else version (DragonFlyBSD) +{ + enum SHMLBA = 1 << 12; // PAGE_SIZE = (1<<PAGE_SHIFT) - version (X86_64) - enum includeUnused = false; - else version (MIPS32) - enum includeUnused = false; - else - enum includeUnused = true; + void* shmat(int, const scope void*, int); + int shmctl(int, int, shmid_ds*); + int shmdt(const scope void*); + int shmget(key_t, size_t, int); +} +else version (Darwin) +{ - struct shmid_ds - { - ipc_perm shm_perm; - size_t shm_segsz; - time_t shm_atime; - static if (includeUnused) c_ulong __unused1; - time_t shm_dtime; - static if (includeUnused) c_ulong __unused2; - time_t shm_ctime; - static if (includeUnused) c_ulong __unused3; - pid_t shm_cpid; - pid_t shm_lpid; - shmatt_t shm_nattch; - c_ulong __unused4; - c_ulong __unused5; - } +} +else version (Solaris) +{ - struct shminfo - { - c_ulong shmmax; - c_ulong shmmin; - c_ulong shmmni; - c_ulong shmseg; - c_ulong shmall; - c_ulong __unused1; - c_ulong __unused2; - c_ulong __unused3; - c_ulong __unused4; - } +} +else version (CRuntime_Musl) +{ + enum SHMLBA = 4096; - struct shm_info - { - int used_ids; - c_ulong shm_tot; - c_ulong shm_rss; - c_ulong shm_swp; - c_ulong swap_attempts; - c_ulong swap_successes; - } + void* shmat(int, const scope void*, int); + int shmctl(int, int, shmid_ds*); + int shmdt(const scope void*); + int shmget(key_t, size_t, int); +} +else version (CRuntime_UClibc) +{ + int __getpagesize(); + alias __getpagesize SHMLBA; void* shmat(int, const scope void*, int); int shmctl(int, int, shmid_ds*); int shmdt(const scope void*); int shmget(key_t, size_t, int); } +else +{ + static assert(false, "Unsupported platform"); +} diff --git a/libphobos/libdruntime/core/sys/posix/sys/socket.d b/libphobos/libdruntime/core/sys/posix/sys/socket.d index 6e0cfd3ac3d..670ead73c64 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/socket.d +++ b/libphobos/libdruntime/core/sys/posix/sys/socket.d @@ -137,31 +137,10 @@ AF_UNSPEC SHUT_RD SHUT_RDWR SHUT_WR - -int accept(int, sockaddr*, socklen_t*); -int bind(int, const scope sockaddr*, socklen_t); -int connect(int, const scope sockaddr*, socklen_t); -int getpeername(int, sockaddr*, socklen_t*); -int getsockname(int, sockaddr*, socklen_t*); -int getsockopt(int, int, int, void*, socklen_t*); -int listen(int, int); -ssize_t recv(int, void*, size_t, int); -ssize_t recvfrom(int, void*, size_t, int, sockaddr*, socklen_t*); -ssize_t recvmsg(int, msghdr*, int); -ssize_t send(int, const scope void*, size_t, int); -ssize_t sendmsg(int, const scope msghdr*, int); -ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); -int setsockopt(int, int, int, const scope void*, socklen_t); -int shutdown(int, int); -int socket(int, int, int); -int sockatmark(int); -int socketpair(int, int, int, ref int[2]); */ -version (CRuntime_Glibc) +version (linux) { - // Some of the constants below and from the Bionic section are really from - // the linux kernel headers. alias uint socklen_t; alias ushort sa_family_t; @@ -174,14 +153,14 @@ version (CRuntime_Glibc) private enum : size_t { _SS_SIZE = 128, - _SS_PADSIZE = _SS_SIZE - (c_ulong.sizeof * 2) + _SS_PADSIZE = _SS_SIZE - c_ulong.sizeof - sa_family_t.sizeof } struct sockaddr_storage { sa_family_t ss_family; - c_ulong __ss_align; byte[_SS_PADSIZE] __ss_padding; + c_ulong __ss_align; } struct msghdr @@ -200,10 +179,6 @@ version (CRuntime_Glibc) size_t cmsg_len; int cmsg_level; int cmsg_type; - static if ( false /* (!is( __STRICT_ANSI__ ) && __GNUC__ >= 2) || __STDC_VERSION__ >= 199901L */ ) - { - ubyte[1] __cmsg_data; - } } enum : uint @@ -211,14 +186,7 @@ version (CRuntime_Glibc) SCM_RIGHTS = 0x01 } - static if ( false /* (!is( __STRICT_ANSI__ ) && __GNUC__ >= 2) || __STDC_VERSION__ >= 199901L */ ) - { - extern (D) ubyte[1] CMSG_DATA( cmsghdr* cmsg ) pure nothrow @nogc { return cmsg.__cmsg_data; } - } - else - { - extern (D) inout(ubyte)* CMSG_DATA( return scope inout(cmsghdr)* cmsg ) pure nothrow @nogc { return cast(ubyte*)( cmsg + 1 ); } - } + extern (D) inout(ubyte)* CMSG_DATA( return scope inout(cmsghdr)* cmsg ) pure nothrow @nogc { return cast(ubyte*)( cmsg + 1 ); } private inout(cmsghdr)* __cmsg_nxthdr(inout(msghdr)*, inout(cmsghdr)*) pure nothrow @nogc; extern (D) inout(cmsghdr)* CMSG_NXTHDR(inout(msghdr)* msg, inout(cmsghdr)* cmsg) pure nothrow @nogc @@ -534,10 +502,19 @@ version (CRuntime_Glibc) else static assert(0, "unimplemented"); - enum + version (CRuntime_Glibc) + { + enum + { + SOMAXCONN = 4096 + } + } + else { - // https://sourceware.org/git/?p=glibc.git;a=commit;f=sysdeps/unix/sysv/linux/bits/socket.h;h=96958e2700f5b4f4d1183a0606b2b9848a53ea44 - SOMAXCONN = 4096 + enum + { + SOMAXCONN = 128 + } } enum : uint @@ -571,25 +548,6 @@ version (CRuntime_Glibc) SHUT_WR, SHUT_RDWR } - - int accept(int, scope sockaddr*, scope socklen_t*); - int bind(int, const scope sockaddr*, socklen_t); - int connect(int, const scope sockaddr*, socklen_t); - int getpeername(int, scope sockaddr*, scope socklen_t*); - int getsockname(int, scope sockaddr*, scope socklen_t*); - int getsockopt(int, int, int, scope void*, scope socklen_t*); - int listen(int, int) @safe; - ssize_t recv(int, scope void*, size_t, int); - ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); - ssize_t recvmsg(int, scope msghdr*, int); - ssize_t send(int, const scope void*, size_t, int); - ssize_t sendmsg(int, const scope msghdr*, int); - ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); - int setsockopt(int, int, int, const scope void*, socklen_t); - int shutdown(int, int) @safe; - int socket(int, int, int) @safe; - int sockatmark(int) @safe; - int socketpair(int, int, int, ref int[2]) @safe; } else version (Darwin) { @@ -727,25 +685,6 @@ else version (Darwin) SHUT_WR, SHUT_RDWR } - - int accept(int, scope sockaddr*, scope socklen_t*); - int bind(int, const scope sockaddr*, socklen_t); - int connect(int, const scope sockaddr*, socklen_t); - int getpeername(int, scope sockaddr*, scope socklen_t*); - int getsockname(int, scope sockaddr*, scope socklen_t*); - int getsockopt(int, int, int, scope void*, scope socklen_t*); - int listen(int, int) @safe; - ssize_t recv(int, scope void*, size_t, int); - ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); - ssize_t recvmsg(int, scope msghdr*, int); - ssize_t send(int, const scope void*, size_t, int); - ssize_t sendmsg(int, const scope msghdr*, int); - ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); - int setsockopt(int, int, int, const scope void*, socklen_t); - int shutdown(int, int) @safe; - int socket(int, int, int) @safe; - int sockatmark(int) @safe; - int socketpair(int, int, int, ref int[2]) @safe; } else version (FreeBSD) { @@ -904,25 +843,6 @@ else version (FreeBSD) SHUT_WR = 1, SHUT_RDWR = 2 } - - int accept(int, scope sockaddr*, scope socklen_t*); - int bind(int, const scope sockaddr*, socklen_t); - int connect(int, const scope sockaddr*, socklen_t); - int getpeername(int, scope sockaddr*, scope socklen_t*); - int getsockname(int, scope sockaddr*, scope socklen_t*); - int getsockopt(int, int, int, scope void*, scope socklen_t*); - int listen(int, int) @safe; - ssize_t recv(int, scope void*, size_t, int); - ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); - ssize_t recvmsg(int, scope msghdr*, int); - ssize_t send(int, const scope void*, size_t, int); - ssize_t sendmsg(int, const scope msghdr*, int); - ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); - int setsockopt(int, int, int, const scope void*, socklen_t); - int shutdown(int, int) @safe; - int socket(int, int, int) @safe; - int sockatmark(int) @safe; - int socketpair(int, int, int, ref int[2]) @safe; } else version (NetBSD) { @@ -1101,25 +1021,6 @@ else version (NetBSD) SHUT_WR = 1, SHUT_RDWR = 2 } - - int accept(int, scope sockaddr*, scope socklen_t*); - int bind(int, const scope sockaddr*, socklen_t); - int connect(int, const scope sockaddr*, socklen_t); - int getpeername(int, scope sockaddr*, scope socklen_t*); - int getsockname(int, scope sockaddr*, scope socklen_t*); - int getsockopt(int, int, int, scope void*, scope socklen_t*); - int listen(int, int) @safe; - ssize_t recv(int, scope void*, size_t, int); - ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); - ssize_t recvmsg(int, scope msghdr*, int); - ssize_t send(int, const scope void*, size_t, int); - ssize_t sendmsg(int, const scope msghdr*, int); - ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); - int setsockopt(int, int, int, const scope void*, socklen_t); - int shutdown(int, int) @safe; - int socket(int, int, int) @safe; - int sockatmark(int) @safe; - int socketpair(int, int, int, ref int[2]) @safe; } else version (OpenBSD) { @@ -1274,25 +1175,6 @@ else version (OpenBSD) SHUT_WR = 1, SHUT_RDWR = 2 } - - int accept(int, scope sockaddr*, scope socklen_t*); - int bind(int, const scope sockaddr*, socklen_t); - int connect(int, const scope sockaddr*, socklen_t); - int getpeername(int, scope sockaddr*, scope socklen_t*); - int getsockname(int, scope sockaddr*, scope socklen_t*); - int getsockopt(int, int, int, scope void*, scope socklen_t*); - int listen(int, int) @safe; - ssize_t recv(int, scope void*, size_t, int); - ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); - ssize_t recvmsg(int, scope msghdr*, int); - ssize_t send(int, const scope void*, size_t, int); - ssize_t sendmsg(int, const scope msghdr*, int); - ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); - int setsockopt(int, int, int, const scope void*, socklen_t); - int shutdown(int, int) @safe; - int socket(int, int, int) @safe; - int sockatmark(int) @safe; - int socketpair(int, int, int, ref int[2]) @safe; } else version (DragonFlyBSD) { @@ -1493,39 +1375,6 @@ else version (DragonFlyBSD) SHUT_WR = 1, SHUT_RDWR = 2 } - -/* - /+ sendfile(2) header/trailer struct +/ - struct sf_hdtr { - iovec * headers; - int hdr_cnt; - iovec * trailers; - int trl_cnt; - } -*/ - - int accept(int, sockaddr*, socklen_t*); -// int accept4(int, sockaddr*, socklen_t*, int); - int bind(int, const scope sockaddr*, socklen_t); - int connect(int, const scope sockaddr*, socklen_t); -// int extconnect(int, int, sockaddr*, socklen_t); - int getpeername(int, sockaddr*, socklen_t*); - int getsockname(int, sockaddr*, socklen_t*); - int getsockopt(int, int, int, void*, socklen_t*); - int listen(int, int); - ssize_t recv(int, void*, size_t, int); - ssize_t recvfrom(int, void*, size_t, int, sockaddr*, socklen_t*); - ssize_t recvmsg(int, msghdr*, int); - ssize_t send(int, const scope void*, size_t, int); - ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); - ssize_t sendmsg(int, const scope msghdr*, int); -// int sendfile(int, int, off_t, size_t, sf_hdtr *, off_t *, int); - int setsockopt(int, int, int, const scope void*, socklen_t); - int shutdown(int, int); - int sockatmark(int); - int socket(int, int, int); - int socketpair(int, int, int, ref int[2]); -// void pfctlinput(int, struct sockaddr *); } else version (Solaris) { @@ -1655,7 +1504,165 @@ else version (Solaris) SHUT_WR, SHUT_RDWR } +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +int accept(int, sockaddr*, socklen_t*); +int bind(int, const scope sockaddr*, socklen_t); +int connect(int, const scope sockaddr*, socklen_t); +int getpeername(int, sockaddr*, socklen_t*); +int getsockname(int, sockaddr*, socklen_t*); +int getsockopt(int, int, int, void*, socklen_t*); +int listen(int, int); +ssize_t recv(int, void*, size_t, int); +ssize_t recvfrom(int, void*, size_t, int, sockaddr*, socklen_t*); +ssize_t recvmsg(int, msghdr*, int); +ssize_t send(int, const scope void*, size_t, int); +ssize_t sendmsg(int, const scope msghdr*, int); +ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); +int setsockopt(int, int, int, const scope void*, socklen_t); +int shutdown(int, int); +int socket(int, int, int); +int sockatmark(int); +int socketpair(int, int, int, ref int[2]); +*/ +version (CRuntime_Glibc) +{ + int accept(int, scope sockaddr*, scope socklen_t*); + int bind(int, const scope sockaddr*, socklen_t); + int connect(int, const scope sockaddr*, socklen_t); + int getpeername(int, scope sockaddr*, scope socklen_t*); + int getsockname(int, scope sockaddr*, scope socklen_t*); + int getsockopt(int, int, int, scope void*, scope socklen_t*); + int listen(int, int) @safe; + ssize_t recv(int, scope void*, size_t, int); + ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); + ssize_t recvmsg(int, scope msghdr*, int); + ssize_t send(int, const scope void*, size_t, int); + ssize_t sendmsg(int, const scope msghdr*, int); + ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); + int setsockopt(int, int, int, const scope void*, socklen_t); + int shutdown(int, int) @safe; + int socket(int, int, int) @safe; + int sockatmark(int) @safe; + int socketpair(int, int, int, ref int[2]) @safe; +} +else version (Darwin) +{ + int accept(int, scope sockaddr*, scope socklen_t*); + int bind(int, const scope sockaddr*, socklen_t); + int connect(int, const scope sockaddr*, socklen_t); + int getpeername(int, scope sockaddr*, scope socklen_t*); + int getsockname(int, scope sockaddr*, scope socklen_t*); + int getsockopt(int, int, int, scope void*, scope socklen_t*); + int listen(int, int) @safe; + ssize_t recv(int, scope void*, size_t, int); + ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); + ssize_t recvmsg(int, scope msghdr*, int); + ssize_t send(int, const scope void*, size_t, int); + ssize_t sendmsg(int, const scope msghdr*, int); + ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); + int setsockopt(int, int, int, const scope void*, socklen_t); + int shutdown(int, int) @safe; + int socket(int, int, int) @safe; + int sockatmark(int) @safe; + int socketpair(int, int, int, ref int[2]) @safe; +} +else version (FreeBSD) +{ + int accept(int, scope sockaddr*, scope socklen_t*); + int bind(int, const scope sockaddr*, socklen_t); + int connect(int, const scope sockaddr*, socklen_t); + int getpeername(int, scope sockaddr*, scope socklen_t*); + int getsockname(int, scope sockaddr*, scope socklen_t*); + int getsockopt(int, int, int, scope void*, scope socklen_t*); + int listen(int, int) @safe; + ssize_t recv(int, scope void*, size_t, int); + ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); + ssize_t recvmsg(int, scope msghdr*, int); + ssize_t send(int, const scope void*, size_t, int); + ssize_t sendmsg(int, const scope msghdr*, int); + ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); + int setsockopt(int, int, int, const scope void*, socklen_t); + int shutdown(int, int) @safe; + int socket(int, int, int) @safe; + int sockatmark(int) @safe; + int socketpair(int, int, int, ref int[2]) @safe; +} +else version (NetBSD) +{ + int accept(int, scope sockaddr*, scope socklen_t*); + int bind(int, const scope sockaddr*, socklen_t); + int connect(int, const scope sockaddr*, socklen_t); + int getpeername(int, scope sockaddr*, scope socklen_t*); + int getsockname(int, scope sockaddr*, scope socklen_t*); + int getsockopt(int, int, int, scope void*, scope socklen_t*); + int listen(int, int) @safe; + ssize_t recv(int, scope void*, size_t, int); + ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); + ssize_t recvmsg(int, scope msghdr*, int); + ssize_t send(int, const scope void*, size_t, int); + ssize_t sendmsg(int, const scope msghdr*, int); + ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); + int setsockopt(int, int, int, const scope void*, socklen_t); + int shutdown(int, int) @safe; + int socket(int, int, int) @safe; + int sockatmark(int) @safe; + int socketpair(int, int, int, ref int[2]) @safe; +} +else version (OpenBSD) +{ + int accept(int, scope sockaddr*, scope socklen_t*); + int bind(int, const scope sockaddr*, socklen_t); + int connect(int, const scope sockaddr*, socklen_t); + int getpeername(int, scope sockaddr*, scope socklen_t*); + int getsockname(int, scope sockaddr*, scope socklen_t*); + int getsockopt(int, int, int, scope void*, scope socklen_t*); + int listen(int, int) @safe; + ssize_t recv(int, scope void*, size_t, int); + ssize_t recvfrom(int, scope void*, size_t, int, scope sockaddr*, scope socklen_t*); + ssize_t recvmsg(int, scope msghdr*, int); + ssize_t send(int, const scope void*, size_t, int); + ssize_t sendmsg(int, const scope msghdr*, int); + ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); + int setsockopt(int, int, int, const scope void*, socklen_t); + int shutdown(int, int) @safe; + int socket(int, int, int) @safe; + int sockatmark(int) @safe; + int socketpair(int, int, int, ref int[2]) @safe; +} +else version (DragonFlyBSD) +{ + int accept(int, sockaddr*, socklen_t*); +// int accept4(int, sockaddr*, socklen_t*, int); + int bind(int, const scope sockaddr*, socklen_t); + int connect(int, const scope sockaddr*, socklen_t); +// int extconnect(int, int, sockaddr*, socklen_t); + int getpeername(int, sockaddr*, socklen_t*); + int getsockname(int, sockaddr*, socklen_t*); + int getsockopt(int, int, int, void*, socklen_t*); + int listen(int, int); + ssize_t recv(int, void*, size_t, int); + ssize_t recvfrom(int, void*, size_t, int, sockaddr*, socklen_t*); + ssize_t recvmsg(int, msghdr*, int); + ssize_t send(int, const scope void*, size_t, int); + ssize_t sendto(int, const scope void*, size_t, int, const scope sockaddr*, socklen_t); + ssize_t sendmsg(int, const scope msghdr*, int); +// int sendfile(int, int, off_t, size_t, sf_hdtr *, off_t *, int); + int setsockopt(int, int, int, const scope void*, socklen_t); + int shutdown(int, int); + int sockatmark(int); + int socket(int, int, int); + int socketpair(int, int, int, ref int[2]); +// void pfctlinput(int, struct sockaddr *); +} +else version (Solaris) +{ int accept(int, scope sockaddr*, scope socklen_t*); int bind(int, const scope sockaddr*, socklen_t); int connect(int, const scope sockaddr*, socklen_t); @@ -1677,146 +1684,6 @@ else version (Solaris) } else version (CRuntime_Bionic) { - alias int socklen_t; - alias ushort sa_family_t; - - struct sockaddr - { - sa_family_t sa_family; - byte[14] sa_data; - } - - private enum size_t _K_SS_MAXSIZE = 128; - - struct sockaddr_storage - { - ushort ss_family; - byte[_K_SS_MAXSIZE - ushort.sizeof] __data; - } - - enum : uint - { - SCM_RIGHTS = 0x01 - } - - private enum _ALIGNBYTES = c_long.sizeof - 1; - - extern (D) - { - size_t CMSG_ALIGN( size_t len ) - { - return (len + _ALIGNBYTES) & ~_ALIGNBYTES; - } - - void* CMSG_DATA( cmsghdr* cmsg ) - { - return cast(void*) (cast(char*) cmsg + CMSG_ALIGN( cmsghdr.sizeof )); - } - - cmsghdr* CMSG_NXTHDR( msghdr* mhdr, cmsghdr* cmsg ) - { - cmsghdr* __ptr = cast(cmsghdr*) ((cast(ubyte*) cmsg) + CMSG_ALIGN(cmsg.cmsg_len)); - return cast(c_ulong)( cast(char*)(__ptr+1) - cast(char*) mhdr.msg_control) > mhdr.msg_controllen ? null : __ptr; - } - - cmsghdr* CMSG_FIRSTHDR( msghdr* mhdr ) - { - return mhdr.msg_controllen >= cmsghdr.sizeof ? cast(cmsghdr*) mhdr.msg_control : null; - } - } - - struct linger - { - int l_onoff; - int l_linger; - } - - struct msghdr - { - void* msg_name; - int msg_namelen; - iovec* msg_iov; - __kernel_size_t msg_iovlen; - void* msg_control; - __kernel_size_t msg_controllen; - uint msg_flags; - } - - struct cmsghdr - { - __kernel_size_t cmsg_len; - int cmsg_level; - int cmsg_type; - } - - alias size_t __kernel_size_t; - - enum - { - SOCK_DGRAM = 2, - SOCK_SEQPACKET = 5, - SOCK_STREAM = 1 - } - - enum - { - SOL_SOCKET = 1 - } - - enum - { - SO_ACCEPTCONN = 30, - SO_BROADCAST = 6, - SO_DEBUG = 1, - SO_DONTROUTE = 5, - SO_ERROR = 4, - SO_KEEPALIVE = 9, - SO_LINGER = 13, - SO_OOBINLINE = 10, - SO_RCVBUF = 8, - SO_RCVLOWAT = 18, - SO_RCVTIMEO = 20, - SO_REUSEADDR = 2, - SO_SNDBUF = 7, - SO_SNDLOWAT = 19, - SO_SNDTIMEO = 21, - SO_TYPE = 3 - } - - enum - { - SOMAXCONN = 128 - } - - enum : uint - { - MSG_CTRUNC = 0x08, - MSG_DONTROUTE = 0x04, - MSG_EOR = 0x80, - MSG_OOB = 0x01, - MSG_PEEK = 0x02, - MSG_TRUNC = 0x20, - MSG_WAITALL = 0x100 - } - - enum - { - AF_APPLETALK = 5, - AF_INET = 2, - AF_IPX = 4, - AF_UNIX = 1, - AF_UNSPEC = 0 - } - - enum - { - SHUT_RD, - SHUT_WR, - SHUT_RDWR - } - - enum SOCK_RDM = 4; - int accept(int, scope sockaddr*, scope socklen_t*); int bind(int, const scope sockaddr*, socklen_t); int connect(int, const scope sockaddr*, socklen_t); @@ -1838,139 +1705,6 @@ else version (CRuntime_Bionic) } else version (CRuntime_Musl) { - alias uint socklen_t; - alias ushort sa_family_t; - - struct sockaddr - { - sa_family_t sa_family; - byte[14] sa_data; - } - - private enum : size_t - { - _SS_SIZE = 128, - _SS_PADSIZE = _SS_SIZE - c_ulong.sizeof - sa_family_t.sizeof - } - - struct sockaddr_storage - { - sa_family_t ss_family; - byte[_SS_PADSIZE] __ss_padding; - c_ulong __ss_align; - } - - enum - { - SOCK_STREAM = 1, - SOCK_DGRAM = 2, - SOCK_RDM = 4, - SOCK_SEQPACKET = 5, - SOCK_DCCP = 6, - SOCK_PACKET = 10 - } - enum - { - AF_UNSPEC = 0, - AF_LOCAL = 1, - AF_UNIX = AF_LOCAL, - AF_FILE = AF_LOCAL, - AF_INET = 2, - AF_AX25 = 3, - AF_IPX = 4, - AF_APPLETALK = 5, - PF_APPLETALK = AF_APPLETALK, - PF_IPX = AF_IPX - } - - enum - { - SHUT_RD, - SHUT_WR, - SHUT_RDWR - } - - enum - { - SOL_SOCKET = 1 - } - - enum - { - SO_DEBUG = 1 - } - - version (MIPS_Any) - { - enum - { - SO_REUSEADDR = 0x0004, - SO_TYPE = 0x1008, - SO_ERROR = 0x1007, - SO_DONTROUTE = 0x0010, - SO_BROADCAST = 0x0020, - SO_SNDBUF = 0x1001, - SO_RCVBUF = 0x1002, - SO_KEEPALIVE = 0x0008, - SO_OOBINLINE = 0x0100, - SO_LINGER = 0x0080, - SO_REUSEPORT = 0x0200, - SO_RCVLOWAT = 0x1004, - SO_SNDLOWAT = 0x1003, - SO_RCVTIMEO = 0x1006, - SO_SNDTIMEO = 0x1005, - SO_ACCEPTCONN = 0x1009 - } - } - else - { - enum - { - SO_REUSEADDR = 2, - SO_TYPE = 3, - SO_ERROR = 4, - SO_DONTROUTE = 5, - SO_BROADCAST = 6, - SO_SNDBUF = 7, - SO_RCVBUF = 8, - SO_KEEPALIVE = 9, - SO_OOBINLINE = 10, - SO_LINGER = 13, - SO_REUSEPORT = 15, - SO_RCVLOWAT = 18, - SO_SNDLOWAT = 19, - SO_RCVTIMEO = 20, - SO_SNDTIMEO = 21, - SO_ACCEPTCONN = 30 - } - } - - enum : uint - { - MSG_OOB = 0x01, - MSG_PEEK = 0x02, - MSG_DONTROUTE = 0x04, - MSG_CTRUNC = 0x08, - MSG_TRUNC = 0x20, - MSG_EOR = 0x80, - MSG_WAITALL = 0x100, - MSG_NOSIGNAL = 0x4000 - } - - struct linger - { - int l_onoff; - int l_linger; - } - struct msghdr { - void *msg_name; - socklen_t msg_namelen; - iovec *msg_iov; - int msg_iovlen, __pad1; - void *msg_control; - socklen_t msg_controllen, __pad2; - int msg_flags; - } int accept(int, sockaddr*, socklen_t*); int bind(int, const scope sockaddr*, socklen_t); int connect(int, const scope sockaddr*, socklen_t); @@ -1992,182 +1726,6 @@ else version (CRuntime_Musl) } else version (CRuntime_UClibc) { - alias uint socklen_t; - alias ushort sa_family_t; - - struct sockaddr - { - sa_family_t sa_family; - byte[14] sa_data; - } - - private enum : size_t - { - _SS_SIZE = 128, - _SS_PADSIZE = _SS_SIZE - (c_ulong.sizeof * 2) - } - - struct sockaddr_storage - { - sa_family_t ss_family; - c_ulong __ss_align; - byte[_SS_PADSIZE] __ss_padding; - } - - struct msghdr - { - void* msg_name; - socklen_t msg_namelen; - iovec* msg_iov; - size_t msg_iovlen; - void* msg_control; - size_t msg_controllen; - int msg_flags; - } - - struct cmsghdr - { - size_t cmsg_len; - int cmsg_level; - int cmsg_type; - } - - enum : uint - { - SCM_RIGHTS = 0x01 - } - - extern (D) inout(ubyte)* CMSG_DATA( inout(cmsghdr)* cmsg ) pure nothrow @nogc { return cast(ubyte*)( cmsg + 1 ); } - - private inout(cmsghdr)* __cmsg_nxthdr(inout(msghdr)*, inout(cmsghdr)*) pure nothrow @nogc; - extern (D) inout(cmsghdr)* CMSG_NXTHDR(inout(msghdr)* msg, inout(cmsghdr)* cmsg) pure nothrow @nogc - { - return __cmsg_nxthdr(msg, cmsg); - } - - extern (D) inout(cmsghdr)* CMSG_FIRSTHDR( inout(msghdr)* mhdr ) pure nothrow @nogc - { - return ( cast(size_t)mhdr.msg_controllen >= cmsghdr.sizeof - ? cast(inout(cmsghdr)*) mhdr.msg_control - : cast(inout(cmsghdr)*) null ); - } - - extern (D) - { - size_t CMSG_ALIGN( size_t len ) pure nothrow @nogc - { - return (len + size_t.sizeof - 1) & cast(size_t) (~(size_t.sizeof - 1)); - } - - size_t CMSG_LEN( size_t len ) pure nothrow @nogc - { - return CMSG_ALIGN(cmsghdr.sizeof) + len; - } - } - - extern (D) size_t CMSG_SPACE(size_t len) pure nothrow @nogc - { - return CMSG_ALIGN(len) + CMSG_ALIGN(cmsghdr.sizeof); - } - - struct linger - { - int l_onoff; - int l_linger; - } - - version (X86_Any) - { - enum - { - SOCK_DGRAM = 2, - SOCK_SEQPACKET = 5, - SOCK_STREAM = 1, - SOCK_CLOEXEC = 0x80000, // octal 02000000 - SOCK_NONBLOCK = 0x800 // octal 00004000 - } - } - else version (MIPS_Any) - { - enum - { - SOCK_DGRAM = 1, - SOCK_SEQPACKET = 5, - SOCK_STREAM = 2, - SOCK_CLOEXEC = 0x80000, // octal 02000000 - SOCK_NONBLOCK = 0x80 // octal 00000200 - } - } - else version (ARM_Any) - { - enum - { - SOCK_DGRAM = 2, - SOCK_SEQPACKET = 5, - SOCK_STREAM = 1, - SOCK_CLOEXEC = 0x80000, // octal 02000000 - SOCK_NONBLOCK = 0x800 // octal 00004000 - } - } - else - static assert(0, "unimplemented"); - - enum - { - SO_ACCEPTCONN = 30, - SO_BROADCAST = 6, - SO_DEBUG = 1, - SO_DONTROUTE = 5, - SO_ERROR = 4, - SO_KEEPALIVE = 9, - SO_LINGER = 13, - SO_OOBINLINE = 10, - SO_RCVBUF = 8, - SO_RCVLOWAT = 18, - SO_RCVTIMEO = 20, - SO_REUSEADDR = 2, - SO_SNDBUF = 7, - SO_SNDLOWAT = 19, - SO_SNDTIMEO = 21, - SO_TYPE = 3, - - SOL_SOCKET = 1, - SOL_TCP = 6, - SOMAXCONN = 128 - } - - enum : uint - { - MSG_CTRUNC = 0x08, - MSG_DONTROUTE = 0x04, - MSG_EOR = 0x80, - MSG_OOB = 0x01, - MSG_PEEK = 0x02, - MSG_TRUNC = 0x20, - MSG_WAITALL = 0x100, - MSG_NOSIGNAL = 0x4000 - } - - enum - { - AF_APPLETALK = 5, - AF_INET = 2, - AF_IPX = 4, - AF_UNIX = 1, - AF_UNSPEC = 0, - PF_APPLETALK = AF_APPLETALK, - PF_IPX = AF_IPX - } - - enum int SOCK_RDM = 4; - - enum - { - SHUT_RD, - SHUT_WR, - SHUT_RDWR - } - int accept(int, sockaddr*, socklen_t*); int bind(int, const scope sockaddr*, socklen_t); int connect(int, const scope sockaddr*, socklen_t); @@ -2199,7 +1757,7 @@ else AF_INET6 */ -version (CRuntime_Glibc) +version (linux) { enum { @@ -2248,24 +1806,6 @@ else version (Solaris) AF_INET6 = 26, } } -else version (CRuntime_Bionic) -{ - enum - { - AF_INET6 = 10 - } -} -else version (CRuntime_Musl) -{ - enum AF_INET6 = 10; -} -else version (CRuntime_UClibc) -{ - enum - { - AF_INET6 = 10 - } -} else { static assert(false, "Unsupported platform"); @@ -2278,7 +1818,7 @@ else SOCK_RAW */ -version (CRuntime_Glibc) +version (linux) { enum { @@ -2327,27 +1867,6 @@ else version (Solaris) SOCK_RAW = 4, } } -else version (CRuntime_Bionic) -{ - enum - { - SOCK_RAW = 3 - } -} -else version (CRuntime_Musl) -{ - enum - { - SOCK_RAW = 3 - } -} -else version (CRuntime_UClibc) -{ - enum - { - SOCK_RAW = 3 - } -} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/stat.d b/libphobos/libdruntime/core/sys/posix/sys/stat.d index 7d0b1708e2d..51455a98e6f 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/stat.d +++ b/libphobos/libdruntime/core/sys/posix/sys/stat.d @@ -55,45 +55,16 @@ struct stat time_t st_ctime; } -S_IRWXU - S_IRUSR - S_IWUSR - S_IXUSR -S_IRWXG - S_IRGRP - S_IWGRP - S_IXGRP -S_IRWXO - S_IROTH - S_IWOTH - S_IXOTH S_ISUID S_ISGID S_ISVTX -S_ISBLK(m) -S_ISCHR(m) -S_ISDIR(m) -S_ISFIFO(m) -S_ISREG(m) -S_ISLNK(m) -S_ISSOCK(m) - S_TYPEISMQ(buf) S_TYPEISSEM(buf) S_TYPEISSHM(buf) - -int chmod(const scope char*, mode_t); -int fchmod(int, mode_t); -int fstat(int, stat*); -int lstat(const scope char*, stat*); -int mkdir(const scope char*, mode_t); -int mkfifo(const scope char*, mode_t); -int stat(const scope char*, stat*); -mode_t umask(mode_t); */ -version (CRuntime_Glibc) +version (linux) { version (X86) { @@ -956,41 +927,10 @@ version (CRuntime_Glibc) else static assert(0, "unimplemented"); - enum S_IRUSR = 0x100; // octal 0400 - enum S_IWUSR = 0x080; // octal 0200 - enum S_IXUSR = 0x040; // octal 0100 - enum S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; - - enum S_IRGRP = S_IRUSR >> 3; - enum S_IWGRP = S_IWUSR >> 3; - enum S_IXGRP = S_IXUSR >> 3; - enum S_IRWXG = S_IRWXU >> 3; - - enum S_IROTH = S_IRGRP >> 3; - enum S_IWOTH = S_IWGRP >> 3; - enum S_IXOTH = S_IXGRP >> 3; - enum S_IRWXO = S_IRWXG >> 3; - enum S_ISUID = 0x800; // octal 04000 enum S_ISGID = 0x400; // octal 02000 enum S_ISVTX = 0x200; // octal 01000 - private - { - extern (D) bool S_ISTYPE( mode_t mode, uint mask ) - { - return ( mode & S_IFMT ) == mask; - } - } - - extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } - extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } - extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } - extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } - extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } - extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } - extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } - static if ( true /*__USE_POSIX199309*/ ) { extern bool S_TYPEISMQ( stat_t* buf ) { return false; } @@ -1000,10 +940,6 @@ version (CRuntime_Glibc) enum UTIME_NOW = 0x3fffffff; enum UTIME_OMIT = 0x3ffffffe; - - int utimensat(int dirfd, const char *pathname, - ref const(timespec)[2] times, int flags); - int futimens(int fd, ref const(timespec)[2] times); } else version (Darwin) { @@ -1049,40 +985,9 @@ else version (Darwin) long[2] st_qspare; } - enum S_IRUSR = 0x100; // octal 0400 - enum S_IWUSR = 0x080; // octal 0200 - enum S_IXUSR = 0x040; // octal 0100 - enum S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; - - enum S_IRGRP = S_IRUSR >> 3; - enum S_IWGRP = S_IWUSR >> 3; - enum S_IXGRP = S_IXUSR >> 3; - enum S_IRWXG = S_IRWXU >> 3; - - enum S_IROTH = S_IRGRP >> 3; - enum S_IWOTH = S_IWGRP >> 3; - enum S_IXOTH = S_IXGRP >> 3; - enum S_IRWXO = S_IRWXG >> 3; - enum S_ISUID = 0x800; // octal 04000 enum S_ISGID = 0x400; // octal 02000 enum S_ISVTX = 0x200; // octal 01000 - - private - { - extern (D) bool S_ISTYPE( mode_t mode, uint mask ) - { - return ( mode & S_IFMT ) == mask; - } - } - - extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } - extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } - extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } - extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } - extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } - extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } - extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } } else version (FreeBSD) { @@ -1164,51 +1069,12 @@ else version (FreeBSD) } } - enum S_IRUSR = 0x100; // octal 0000400 - enum S_IWUSR = 0x080; // octal 0000200 - enum S_IXUSR = 0x040; // octal 0000100 - enum S_IRWXU = 0x1C0; // octal 0000700 - - enum S_IRGRP = 0x020; // octal 0000040 - enum S_IWGRP = 0x010; // octal 0000020 - enum S_IXGRP = 0x008; // octal 0000010 - enum S_IRWXG = 0x038; // octal 0000070 - - enum S_IROTH = 0x4; // 0000004 - enum S_IWOTH = 0x2; // 0000002 - enum S_IXOTH = 0x1; // 0000001 - enum S_IRWXO = 0x7; // 0000007 - enum S_ISUID = 0x800; // octal 0004000 enum S_ISGID = 0x400; // octal 0002000 enum S_ISVTX = 0x200; // octal 0001000 - private - { - extern (D) bool S_ISTYPE( mode_t mode, uint mask ) - { - return ( mode & S_IFMT ) == mask; - } - } - - extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } - extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } - extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } - extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } - extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } - extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } - extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } - enum UTIME_NOW = -1; enum UTIME_OMIT = -2; - - // Since FreeBSD 11: - version (none) - { - int utimensat(int dirfd, const char *pathname, - ref const(timespec)[2] times, int flags); - int futimens(int fd, ref const(timespec)[2] times); - } } else version (NetBSD) { @@ -1237,40 +1103,9 @@ else version (NetBSD) uint32_t[2] st_spare; } - enum S_IRUSR = 0x100; // octal 0000400 - enum S_IWUSR = 0x080; // octal 0000200 - enum S_IXUSR = 0x040; // octal 0000100 - enum S_IRWXU = 0x1C0; // octal 0000700 - - enum S_IRGRP = 0x020; // octal 0000040 - enum S_IWGRP = 0x010; // octal 0000020 - enum S_IXGRP = 0x008; // octal 0000010 - enum S_IRWXG = 0x038; // octal 0000070 - - enum S_IROTH = 0x4; // 0000004 - enum S_IWOTH = 0x2; // 0000002 - enum S_IXOTH = 0x1; // 0000001 - enum S_IRWXO = 0x7; // 0000007 - enum S_ISUID = 0x800; // octal 0004000 enum S_ISGID = 0x400; // octal 0002000 enum S_ISVTX = 0x200; // octal 0001000 - - private - { - extern (D) bool S_ISTYPE( mode_t mode, uint mask ) - { - return ( mode & S_IFMT ) == mask; - } - } - - extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } - extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } - extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } - extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } - extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } - extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } - extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } } else version (OpenBSD) { @@ -1322,32 +1157,9 @@ else version (OpenBSD) } } - enum S_IRUSR = 0x100; // octal 0000400 - enum S_IWUSR = 0x080; // octal 0000200 - enum S_IXUSR = 0x040; // octal 0000100 - enum S_IRWXU = 0x1C0; // octal 0000700 - - enum S_IRGRP = 0x020; // octal 0000040 - enum S_IWGRP = 0x010; // octal 0000020 - enum S_IXGRP = 0x008; // octal 0000010 - enum S_IRWXG = 0x038; // octal 0000070 - - enum S_IROTH = 0x4; // 0000004 - enum S_IWOTH = 0x2; // 0000002 - enum S_IXOTH = 0x1; // 0000001 - enum S_IRWXO = 0x7; // 0000007 - enum S_ISUID = 0x800; // octal 0004000 enum S_ISGID = 0x400; // octal 0002000 enum S_ISVTX = 0x200; // octal 0001000 - - extern (D) bool S_ISBLK(mode_t mode) { return (mode & S_IFMT) == S_IFBLK; } - extern (D) bool S_ISCHR(mode_t mode) { return (mode & S_IFMT) == S_IFCHR; } - extern (D) bool S_ISDIR(mode_t mode) { return (mode & S_IFMT) == S_IFDIR; } - extern (D) bool S_ISFIFO(mode_t mode) { return (mode & S_IFMT) == S_IFIFO; } - extern (D) bool S_ISREG(mode_t mode) { return (mode & S_IFMT) == S_IFREG; } - extern (D) bool S_ISLNK(mode_t mode) { return (mode & S_IFMT) == S_IFLNK; } - extern (D) bool S_ISSOCK(mode_t mode) { return (mode & S_IFMT) == S_IFSOCK; } } else version (DragonFlyBSD) { @@ -1376,40 +1188,9 @@ else version (DragonFlyBSD) int64_t st_qspare2; } - enum S_IRUSR = 0x100; // octal 0000400 - enum S_IWUSR = 0x080; // octal 0000200 - enum S_IXUSR = 0x040; // octal 0000100 - enum S_IRWXU = 0x1C0; // octal 0000700 - - enum S_IRGRP = 0x020; // octal 0000040 - enum S_IWGRP = 0x010; // octal 0000020 - enum S_IXGRP = 0x008; // octal 0000010 - enum S_IRWXG = 0x038; // octal 0000070 - - enum S_IROTH = 0x4; // 0000004 - enum S_IWOTH = 0x2; // 0000002 - enum S_IXOTH = 0x1; // 0000001 - enum S_IRWXO = 0x7; // 0000007 - enum S_ISUID = 0x800; // octal 0004000 enum S_ISGID = 0x400; // octal 0002000 enum S_ISVTX = 0x200; // octal 0001000 - - private - { - extern (D) bool S_ISTYPE( mode_t mode, uint mask ) - { - return ( mode & S_IFMT ) == mask; - } - } - - extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } - extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } - extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } - extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } - extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } - extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } - extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } } else version (Solaris) { @@ -1525,6 +1306,242 @@ else version (Solaris) } + enum S_ISUID = 0x800; + enum S_ISGID = 0x400; + enum S_ISVTX = 0x200; +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +S_IRWXU + S_IRUSR + S_IWUSR + S_IXUSR +S_IRWXG + S_IRGRP + S_IWGRP + S_IXGRP +S_IRWXO + S_IROTH + S_IWOTH + S_IXOTH + +S_ISBLK(m) +S_ISCHR(m) +S_ISDIR(m) +S_ISFIFO(m) +S_ISREG(m) +S_ISLNK(m) +S_ISSOCK(m) + */ + +version (CRuntime_Glibc) +{ + enum S_IRUSR = 0x100; // octal 0400 + enum S_IWUSR = 0x080; // octal 0200 + enum S_IXUSR = 0x040; // octal 0100 + enum S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; + + enum S_IRGRP = S_IRUSR >> 3; + enum S_IWGRP = S_IWUSR >> 3; + enum S_IXGRP = S_IXUSR >> 3; + enum S_IRWXG = S_IRWXU >> 3; + + enum S_IROTH = S_IRGRP >> 3; + enum S_IWOTH = S_IWGRP >> 3; + enum S_IXOTH = S_IXGRP >> 3; + enum S_IRWXO = S_IRWXG >> 3; + + private + { + extern (D) bool S_ISTYPE( mode_t mode, uint mask ) + { + return ( mode & S_IFMT ) == mask; + } + } + + extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } + extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } + extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } + extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } + extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } + extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } + extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } + + int utimensat(int dirfd, const char *pathname, + ref const(timespec)[2] times, int flags); + int futimens(int fd, ref const(timespec)[2] times); +} +else version (Darwin) +{ + enum S_IRUSR = 0x100; // octal 0400 + enum S_IWUSR = 0x080; // octal 0200 + enum S_IXUSR = 0x040; // octal 0100 + enum S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; + + enum S_IRGRP = S_IRUSR >> 3; + enum S_IWGRP = S_IWUSR >> 3; + enum S_IXGRP = S_IXUSR >> 3; + enum S_IRWXG = S_IRWXU >> 3; + + enum S_IROTH = S_IRGRP >> 3; + enum S_IWOTH = S_IWGRP >> 3; + enum S_IXOTH = S_IXGRP >> 3; + enum S_IRWXO = S_IRWXG >> 3; + + private + { + extern (D) bool S_ISTYPE( mode_t mode, uint mask ) + { + return ( mode & S_IFMT ) == mask; + } + } + + extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } + extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } + extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } + extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } + extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } + extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } + extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } +} +else version (FreeBSD) +{ + enum S_IRUSR = 0x100; // octal 0000400 + enum S_IWUSR = 0x080; // octal 0000200 + enum S_IXUSR = 0x040; // octal 0000100 + enum S_IRWXU = 0x1C0; // octal 0000700 + + enum S_IRGRP = 0x020; // octal 0000040 + enum S_IWGRP = 0x010; // octal 0000020 + enum S_IXGRP = 0x008; // octal 0000010 + enum S_IRWXG = 0x038; // octal 0000070 + + enum S_IROTH = 0x4; // 0000004 + enum S_IWOTH = 0x2; // 0000002 + enum S_IXOTH = 0x1; // 0000001 + enum S_IRWXO = 0x7; // 0000007 + + private + { + extern (D) bool S_ISTYPE( mode_t mode, uint mask ) + { + return ( mode & S_IFMT ) == mask; + } + } + + extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } + extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } + extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } + extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } + extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } + extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } + extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } + + // Since FreeBSD 11: + version (none) + { + int utimensat(int dirfd, const char *pathname, + ref const(timespec)[2] times, int flags); + int futimens(int fd, ref const(timespec)[2] times); + } +} +else version (NetBSD) +{ + enum S_IRUSR = 0x100; // octal 0000400 + enum S_IWUSR = 0x080; // octal 0000200 + enum S_IXUSR = 0x040; // octal 0000100 + enum S_IRWXU = 0x1C0; // octal 0000700 + + enum S_IRGRP = 0x020; // octal 0000040 + enum S_IWGRP = 0x010; // octal 0000020 + enum S_IXGRP = 0x008; // octal 0000010 + enum S_IRWXG = 0x038; // octal 0000070 + + enum S_IROTH = 0x4; // 0000004 + enum S_IWOTH = 0x2; // 0000002 + enum S_IXOTH = 0x1; // 0000001 + enum S_IRWXO = 0x7; // 0000007 + + private + { + extern (D) bool S_ISTYPE( mode_t mode, uint mask ) + { + return ( mode & S_IFMT ) == mask; + } + } + + extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } + extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } + extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } + extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } + extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } + extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } + extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } +} +else version (OpenBSD) +{ + enum S_IRUSR = 0x100; // octal 0000400 + enum S_IWUSR = 0x080; // octal 0000200 + enum S_IXUSR = 0x040; // octal 0000100 + enum S_IRWXU = 0x1C0; // octal 0000700 + + enum S_IRGRP = 0x020; // octal 0000040 + enum S_IWGRP = 0x010; // octal 0000020 + enum S_IXGRP = 0x008; // octal 0000010 + enum S_IRWXG = 0x038; // octal 0000070 + + enum S_IROTH = 0x4; // 0000004 + enum S_IWOTH = 0x2; // 0000002 + enum S_IXOTH = 0x1; // 0000001 + enum S_IRWXO = 0x7; // 0000007 + + extern (D) bool S_ISBLK(mode_t mode) { return (mode & S_IFMT) == S_IFBLK; } + extern (D) bool S_ISCHR(mode_t mode) { return (mode & S_IFMT) == S_IFCHR; } + extern (D) bool S_ISDIR(mode_t mode) { return (mode & S_IFMT) == S_IFDIR; } + extern (D) bool S_ISFIFO(mode_t mode) { return (mode & S_IFMT) == S_IFIFO; } + extern (D) bool S_ISREG(mode_t mode) { return (mode & S_IFMT) == S_IFREG; } + extern (D) bool S_ISLNK(mode_t mode) { return (mode & S_IFMT) == S_IFLNK; } + extern (D) bool S_ISSOCK(mode_t mode) { return (mode & S_IFMT) == S_IFSOCK; } +} +else version (DragonFlyBSD) +{ + enum S_IRUSR = 0x100; // octal 0000400 + enum S_IWUSR = 0x080; // octal 0000200 + enum S_IXUSR = 0x040; // octal 0000100 + enum S_IRWXU = 0x1C0; // octal 0000700 + + enum S_IRGRP = 0x020; // octal 0000040 + enum S_IWGRP = 0x010; // octal 0000020 + enum S_IXGRP = 0x008; // octal 0000010 + enum S_IRWXG = 0x038; // octal 0000070 + + enum S_IROTH = 0x4; // 0000004 + enum S_IWOTH = 0x2; // 0000002 + enum S_IXOTH = 0x1; // 0000001 + enum S_IRWXO = 0x7; // 0000007 + + private + { + extern (D) bool S_ISTYPE( mode_t mode, uint mask ) + { + return ( mode & S_IFMT ) == mask; + } + } + + extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } + extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } + extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } + extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } + extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } + extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } + extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } +} +else version (Solaris) +{ enum S_IRUSR = 0x100; enum S_IWUSR = 0x080; enum S_IXUSR = 0x040; @@ -1540,10 +1557,6 @@ else version (Solaris) enum S_IXOTH = 0x1; // 0000001 enum S_IRWXO = 0x7; // 0000007 - enum S_ISUID = 0x800; - enum S_ISGID = 0x400; - enum S_ISVTX = 0x200; - private { extern (D) bool S_ISTYPE(mode_t mode, uint mask) @@ -1564,115 +1577,6 @@ else version (Solaris) } else version (CRuntime_Bionic) { - version (X86) - { - struct stat_t - { - ulong st_dev; - ubyte[4] __pad0; - c_ulong __st_ino; - uint st_mode; - uint st_nlink; - c_ulong st_uid; - c_ulong st_gid; - ulong st_rdev; - ubyte[4] __pad3; - - long st_size; - c_ulong st_blksize; - ulong st_blocks; - c_ulong st_atime; - c_ulong st_atime_nsec; - c_ulong st_mtime; - c_ulong st_mtime_nsec; - c_ulong st_ctime; - c_ulong st_ctime_nsec; - ulong st_ino; - } - } - else version (ARM) - { - struct stat_t - { - ulong st_dev; - ubyte[4] __pad0; - c_ulong __st_ino; - uint st_mode; - uint st_nlink; - c_ulong st_uid; - c_ulong st_gid; - ulong st_rdev; - ubyte[4] __pad3; - - long st_size; - c_ulong st_blksize; - ulong st_blocks; - c_ulong st_atime; - c_ulong st_atime_nsec; - c_ulong st_mtime; - c_ulong st_mtime_nsec; - c_ulong st_ctime; - c_ulong st_ctime_nsec; - ulong st_ino; - } - } - else version (AArch64) - { - struct stat_t - { - ulong st_dev; - ulong st_ino; - uint st_mode; - uint st_nlink; - uid_t st_uid; - gid_t st_gid; - ulong st_rdev; - ulong __pad1; - - long st_size; - int st_blksize; - int __pad2; - long st_blocks; - long st_atime; - ulong st_atime_nsec; - long st_mtime; - ulong st_mtime_nsec; - long st_ctime; - ulong st_ctime_nsec; - uint __unused4; - uint __unused5; - } - } - else version (X86_64) - { - struct stat_t - { - ulong st_dev; - ulong st_ino; - ulong st_nlink; - uint st_mode; - uid_t st_uid; - gid_t st_gid; - uint __pad0; - - ulong st_rdev; - long st_size; - long st_blksize; - long st_blocks; - long st_atime; - ulong st_atime_nsec; - long st_mtime; - ulong st_mtime_nsec; - long st_ctime; - ulong st_ctime_nsec; - long[3] __pad3; - } - } - else - { - static assert(false, "Architecture not supported."); - } - enum S_IRUSR = 0x100; // octal 0000400 enum S_IWUSR = 0x080; // octal 0000200 enum S_IXUSR = 0x040; // octal 0000100 @@ -1688,10 +1592,6 @@ else version (CRuntime_Bionic) enum S_IXOTH = 0x1; // 0000001 enum S_IRWXO = 0x7; // 0000007 - enum S_ISUID = 0x800; // octal 0004000 - enum S_ISGID = 0x400; // octal 0002000 - enum S_ISVTX = 0x200; // octal 0001000 - private { extern (D) bool S_ISTYPE( uint mode, uint mask ) @@ -1730,229 +1630,7 @@ else version (CRuntime_Musl) S_IWOTH = S_IWGRP >> 3, S_IXOTH = S_IXGRP >> 3, S_IRWXO = S_IRWXG >> 3, - - S_ISUID = 0x800, // octal 04000 - S_ISGID = 0x400, // octal 02000 - S_ISVTX = 0x200, // octal 01000 - } - version (ARM) - { - struct stat_t - { - dev_t st_dev; - int __st_dev_padding; - c_long __st_ino_truncated; - mode_t st_mode; - nlink_t st_nlink; - - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - int __st_rdev_padding; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - ino_t st_ino; - - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } - } - else version (AArch64) - { - struct stat_t - { - dev_t st_dev; - ino_t st_ino; - mode_t st_mode; - nlink_t st_nlink; - - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - c_ulong __pad; - off_t st_size; - blksize_t st_blksize; - int __pad2; - blkcnt_t st_blocks; - - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - uint[2] __unused; - - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } - } - else version (X86_64) - { - struct stat_t - { - dev_t st_dev; - ino_t st_ino; - nlink_t st_nlink; - - mode_t st_mode; - uid_t st_uid; - gid_t st_gid; - uint __pad0; - dev_t st_rdev; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - - c_long[3] __unused; - - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } - } - else version (X86) - { - struct stat_t - { - dev_t st_dev; - int __st_dev_padding; - c_long __st_ino_truncated; - mode_t st_mode; - nlink_t st_nlink; - - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - int __st_rdev_padding; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - ino_t st_ino; - - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } - } - else version (MIPS64) - { - struct stat_t - { - dev_t st_dev; - int[3] __pad1; - ino_t st_ino; - mode_t st_mode; - nlink_t st_nlink; - - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - uint[2] __pad2; - off_t st_size; - int __pad3; - - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - blksize_t st_blksize; - uint __pad4; - blkcnt_t st_blocks; - int[14] __pad5; - - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } - } - else version (PPC64) - { - struct stat_t - { - dev_t st_dev; - ino_t st_ino; - nlink_t st_nlink; - mode_t st_mode; - - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - c_ulong[3] __unused; - - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } - } - else version (SystemZ) - { - struct stat_t - { - dev_t st_dev; - ino_t st_ino; - nlink_t st_nlink; - mode_t st_mode; - - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - off_t st_size; - - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - - blksize_t st_blksize; - blkcnt_t st_blocks; - c_ulong[3] __unused; - - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } } - else - static assert("Unsupported platform"); private { @@ -1975,170 +1653,6 @@ else version (CRuntime_Musl) } else version (CRuntime_UClibc) { - version (X86_64) - { - struct stat_t - { - dev_t st_dev; - ino_t st_ino; - nlink_t st_nlink; - mode_t st_mode; - uid_t st_uid; - gid_t st_gid; - uint __pad0; - dev_t st_rdev; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - time_t st_atime; - ulong_t st_atimensec; - time_t st_mtime; - ulong_t st_mtimensec; - time_t st_ctime; - ulong_t st_ctimensec; - slong_t[3] __unused; - } - } - else version (MIPS_O32) - { - struct stat_t - { - c_ulong st_dev; - c_long[3] st_pad1; - ino_t st_ino; - mode_t st_mode; - nlink_t st_nlink; - uid_t st_uid; - gid_t st_gid; - c_ulong st_rdev; - static if (!__USE_FILE_OFFSET64) - { - c_long[2] st_pad2; - off_t st_size; - c_long st_pad3; - } - else - { - c_long[3] st_pad2; - off_t st_size; - } - static if (__USE_MISC || __USE_XOPEN2K8) - { - timespec st_atim; - timespec st_mtim; - timespec st_ctim; - extern(D) @safe @property inout pure nothrow - { - ref inout(time_t) st_atime() return { return st_atim.tv_sec; } - ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } - ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } - } - } - else - { - time_t st_atime; - c_ulong st_atimensec; - time_t st_mtime; - c_ulong st_mtimensec; - time_t st_ctime; - c_ulong st_ctimensec; - } - blksize_t st_blksize; - static if (!__USE_FILE_OFFSET64) - { - blkcnt_t st_blocks; - } - else - { - c_long st_pad4; - blkcnt_t st_blocks; - } - c_long[14] st_pad5; - } - } - else version (ARM) - { - private - { - alias __dev_t = ulong; - alias __ino_t = c_ulong; - alias __ino64_t = ulong; - alias __mode_t = uint; - alias __nlink_t = size_t; - alias __uid_t = uint; - alias __gid_t = uint; - alias __off_t = c_long; - alias __off64_t = long; - alias __blksize_t = c_long; - alias __blkcnt_t = c_long; - alias __blkcnt64_t = long; - alias __timespec = timespec; - alias __time_t = time_t; - } - struct stat_t - { - __dev_t st_dev; - ushort __pad1; - - static if (!__USE_FILE_OFFSET64) - { - __ino_t st_ino; - } - else - { - __ino_t __st_ino; - } - __mode_t st_mode; - __nlink_t st_nlink; - __uid_t st_uid; - __gid_t st_gid; - __dev_t st_rdev; - ushort __pad2; - - static if (!__USE_FILE_OFFSET64) - { - __off_t st_size; - } - else - { - __off64_t st_size; - } - __blksize_t st_blksize; - - static if (!__USE_FILE_OFFSET64) - { - __blkcnt_t st_blocks; - } - else - { - __blkcnt64_t st_blocks; - } - - __time_t st_atime; - c_ulong st_atimensec; - __time_t st_mtime; - c_ulong st_mtimensec; - __time_t st_ctime; - c_ulong st_ctimensec; - - static if (!__USE_FILE_OFFSET64) - { - c_ulong __unused4; - c_ulong __unused5; - } - else - { - __ino64_t st_ino; - } - } - static if (__USE_FILE_OFFSET64) - static assert(stat_t.sizeof == 104); - else - static assert(stat_t.sizeof == 88); - } - else - static assert(0, "unimplemented"); - enum S_IRUSR = 0x100; // octal 0400 enum S_IWUSR = 0x080; // octal 0200 enum S_IXUSR = 0x040; // octal 0100 @@ -2154,10 +1668,6 @@ else version (CRuntime_UClibc) enum S_IXOTH = S_IXGRP >> 3; enum S_IRWXO = S_IRWXG >> 3; - enum S_ISUID = 0x800; // octal 04000 - enum S_ISGID = 0x400; // octal 02000 - enum S_ISVTX = 0x200; // octal 01000 - private { extern (D) bool S_ISTYPE( mode_t mode, uint mask ) @@ -2174,16 +1684,6 @@ else version (CRuntime_UClibc) extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } - static if ( true /*__USE_POSIX199309*/ ) - { - extern bool S_TYPEISMQ( stat_t* buf ) { return false; } - extern bool S_TYPEISSEM( stat_t* buf ) { return false; } - extern bool S_TYPEISSHM( stat_t* buf ) { return false; } - } - - enum UTIME_NOW = 0x3fffffff; - enum UTIME_OMIT = 0x3ffffffe; - int utimensat(int dirfd, const char *pathname, ref const(timespec)[2] times, int flags); int futimens(int fd, ref const(timespec)[2] times); @@ -2193,6 +1693,17 @@ else static assert(false, "Unsupported platform"); } +/* +int chmod(const scope char*, mode_t); +int fchmod(int, mode_t); +int fstat(int, stat*); +int lstat(const scope char*, stat*); +int mkdir(const scope char*, mode_t); +int mkfifo(const scope char*, mode_t); +int stat(const scope char*, stat*); +mode_t umask(mode_t); +*/ + int chmod(const scope char*, mode_t); int fchmod(int, mode_t); //int fstat(int, stat_t*); @@ -2385,11 +1896,9 @@ S_IFREG S_IFDIR S_IFLNK S_IFSOCK - -int mknod(in 3char*, mode_t, dev_t); */ -version (CRuntime_Glibc) +version (linux) { enum S_IFMT = 0xF000; // octal 0170000 enum S_IFBLK = 0x6000; // octal 0060000 @@ -2399,8 +1908,6 @@ version (CRuntime_Glibc) enum S_IFDIR = 0x4000; // octal 0040000 enum S_IFLNK = 0xA000; // octal 0120000 enum S_IFSOCK = 0xC000; // octal 0140000 - - int mknod(const scope char*, mode_t, dev_t); } else version (Darwin) { @@ -2412,8 +1919,6 @@ else version (Darwin) enum S_IFDIR = 0x4000; // octal 0040000 enum S_IFLNK = 0xA000; // octal 0120000 enum S_IFSOCK = 0xC000; // octal 0140000 - - int mknod(const scope char*, mode_t, dev_t); } else version (FreeBSD) { @@ -2425,18 +1930,6 @@ else version (FreeBSD) enum S_IFDIR = 0x4000; // octal 0040000 enum S_IFLNK = 0xA000; // octal 0120000 enum S_IFSOCK = 0xC000; // octal 0140000 - - version (GNU) - { - int mknod(const scope char*, mode_t, dev_t); - } - else - { - static if (__FreeBSD_version >= INO64_FIRST) - pragma(mangle, "mknod@FBSD_1.5") int mknod(const scope char*, mode_t, dev_t); - else - pragma(mangle, "mknod@FBSD_1.0") int mknod(const scope char*, mode_t, dev_t); - } } else version (NetBSD) { @@ -2448,8 +1941,6 @@ else version (NetBSD) enum S_IFDIR = 0x4000; // octal 0040000 enum S_IFLNK = 0xA000; // octal 0120000 enum S_IFSOCK = 0xC000; // octal 0140000 - - int mknod(const scope char*, mode_t, dev_t); } else version (OpenBSD) { @@ -2461,8 +1952,6 @@ else version (OpenBSD) enum S_IFDIR = 0x4000; // octal 0040000 enum S_IFLNK = 0xA000; // octal 0120000 enum S_IFSOCK = 0xC000; // octal 0140000 - - int mknod(const scope char*, mode_t, dev_t); } else version (DragonFlyBSD) { @@ -2474,8 +1963,6 @@ else version (DragonFlyBSD) enum S_IFDIR = 0x4000; // octal 0040000 enum S_IFLNK = 0xA000; // octal 0120000 enum S_IFSOCK = 0xC000; // octal 0140000 - - int mknod(const scope char*, mode_t, dev_t); } else version (Solaris) { @@ -2489,48 +1976,64 @@ else version (Solaris) enum S_IFSOCK = 0xC000; enum S_IFDOOR = 0xD000; enum S_IFPORT = 0xE000; +} +else +{ + static assert(false, "Unsupported platform"); +} +/* +int mknod(const scope char*, mode_t, dev_t); +*/ + +version (CRuntime_Glibc) +{ + int mknod(const scope char*, mode_t, dev_t); +} +else version (Darwin) +{ + int mknod(const scope char*, mode_t, dev_t); +} +else version (FreeBSD) +{ + version (GNU) + { + int mknod(const scope char*, mode_t, dev_t); + } + else + { + static if (__FreeBSD_version >= INO64_FIRST) + pragma(mangle, "mknod@FBSD_1.5") int mknod(const scope char*, mode_t, dev_t); + else + pragma(mangle, "mknod@FBSD_1.0") int mknod(const scope char*, mode_t, dev_t); + } +} +else version (NetBSD) +{ + int mknod(const scope char*, mode_t, dev_t); +} +else version (OpenBSD) +{ + int mknod(const scope char*, mode_t, dev_t); +} +else version (DragonFlyBSD) +{ + int mknod(const scope char*, mode_t, dev_t); +} +else version (Solaris) +{ int mknod(const scope char*, mode_t, dev_t); } else version (CRuntime_Bionic) { - enum S_IFMT = 0xF000; // octal 0170000 - enum S_IFBLK = 0x6000; // octal 0060000 - enum S_IFCHR = 0x2000; // octal 0020000 - enum S_IFIFO = 0x1000; // octal 0010000 - enum S_IFREG = 0x8000; // octal 0100000 - enum S_IFDIR = 0x4000; // octal 0040000 - enum S_IFLNK = 0xA000; // octal 0120000 - enum S_IFSOCK = 0xC000; // octal 0140000 - int mknod(const scope char*, mode_t, dev_t); } else version (CRuntime_Musl) { - enum { - S_IFMT = 0xF000, // octal 0170000 - S_IFBLK = 0x6000, // octal 0060000 - S_IFCHR = 0x2000, // octal 0020000 - S_IFIFO = 0x1000, // octal 0010000 - S_IFREG = 0x8000, // octal 0100000 - S_IFDIR = 0x4000, // octal 0040000 - S_IFLNK = 0xA000, // octal 0120000 - S_IFSOCK = 0xC000, // octal 0140000 - } - int mknod(const scope char*, mode_t, dev_t); } else version (CRuntime_UClibc) { - enum S_IFMT = 0xF000; // octal 0170000 - enum S_IFBLK = 0x6000; // octal 0060000 - enum S_IFCHR = 0x2000; // octal 0020000 - enum S_IFIFO = 0x1000; // octal 0010000 - enum S_IFREG = 0x8000; // octal 0100000 - enum S_IFDIR = 0x4000; // octal 0040000 - enum S_IFLNK = 0xA000; // octal 0120000 - enum S_IFSOCK = 0xC000; // octal 0140000 - int mknod(const scope char*, mode_t, dev_t); } else diff --git a/libphobos/libdruntime/core/sys/posix/sys/types.d b/libphobos/libdruntime/core/sys/posix/sys/types.d index abcea99019f..02cf799ff27 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/types.d +++ b/libphobos/libdruntime/core/sys/posix/sys/types.d @@ -86,7 +86,7 @@ time_t uid_t */ -version (CRuntime_Glibc) +version (linux) { static if ( __USE_FILE_OFFSET64 ) { @@ -108,71 +108,40 @@ version (CRuntime_Glibc) alias int pid_t; //size_t (defined in core.stdc.stddef) alias c_long ssize_t; - alias slong_t time_t; alias uint uid_t; -} -else version (CRuntime_Musl) -{ - version (AArch64) - { - alias int blksize_t; - alias uint nlink_t; - } - else version (MIPS64) - { - alias c_long blksize_t; - alias uint nlink_t; - } - else version (RISCV64) - { - alias int blksize_t; - alias uint nlink_t; + + version (CRuntime_Musl) + { + /** + * Musl versions before v1.2.0 (up to v1.1.24) had different + * definitions for `time_t` for 32 bits. + * This was changed to always be 64 bits in v1.2.0: + * https://musl.libc.org/time64.html + * This change was only for 32 bits system and + * didn't affect 64 bits systems + * + * To check previous definitions, `grep` for `time_t` in `arch/`, + * and the result should be (in v1.1.24): + * --- + * // arch/riscv64/bits/alltypes.h.in:20:TYPEDEF long time_t; + * // arch/s390x/bits/alltypes.h.in:17:TYPEDEF long time_t; + * // arch/sh/bits/alltypes.h.in:21:TYPEDEF long time_t; + * --- + * + * In order to be compatible with old versions of Musl, + * one can recompile druntime with `CRuntime_Musl_Pre_Time64`. + */ + version (D_X32) + alias long time_t; + else version (CRuntime_Musl_Pre_Time64) + alias c_long time_t; + else + alias long time_t; } else { - alias c_long blksize_t; - alias c_ulong nlink_t; - } - alias long dev_t; - alias long blkcnt_t; - alias ulong ino_t; - alias long off_t; - alias int pid_t; - alias uint uid_t; - alias uint gid_t; - - /** - * Musl versions before v1.2.0 (up to v1.1.24) had different - * definitions for `time_t` for 32 bits. - * This was changed to always be 64 bits in v1.2.0: - * https://musl.libc.org/time64.html - * This change was only for 32 bits system and - * didn't affect 64 bits systems - * - * To check previous definitions, `grep` for `time_t` in `arch/`, - * and the result should be (in v1.1.24): - * --- - * // arch/riscv64/bits/alltypes.h.in:20:TYPEDEF long time_t; - * // arch/s390x/bits/alltypes.h.in:17:TYPEDEF long time_t; - * // arch/sh/bits/alltypes.h.in:21:TYPEDEF long time_t; - * --- - * - * In order to be compatible with old versions of Musl, - * one can recompile druntime with `CRuntime_Musl_Pre_Time64`. - */ - version (D_X32) - alias long time_t; - else version (CRuntime_Musl_Pre_Time64) - alias c_long time_t; - else - alias long time_t; - - alias c_long clock_t; - alias c_ulong pthread_t; - version (D_LP64) - alias c_long ssize_t; - else - alias int ssize_t; + alias slong_t time_t; + } } else version (Darwin) { @@ -311,67 +280,6 @@ else version (Solaris) alias c_long time_t; alias uint uid_t; } -else version (CRuntime_Bionic) -{ - alias c_ulong blkcnt_t; - alias c_ulong blksize_t; - alias size_t dev_t; - alias uint gid_t; - alias c_ulong ino_t; - alias c_long off_t; - alias int pid_t; - alias c_long ssize_t; - alias c_long time_t; - alias uint uid_t; - - version (D_LP64) - { - alias uint mode_t; - alias uint nlink_t; - } - else - { - alias ushort mode_t; - alias ushort nlink_t; - } -} -else version (CRuntime_UClibc) -{ - static if ( __USE_FILE_OFFSET64 ) - { - alias long blkcnt_t; - alias ulong ino_t; - alias long off_t; - } - else - { - alias slong_t blkcnt_t; - alias ulong_t ino_t; - alias slong_t off_t; - } - - version (D_LP64) - { - alias ino_t ino64_t; - alias off_t off64_t; - } - else - { - alias ulong ino64_t; - alias long off64_t; - } - - alias slong_t blksize_t; - alias c_ulong dev_t; - alias uint gid_t; - alias uint mode_t; - alias uint nlink_t; - alias int pid_t; - //size_t (defined in core.stdc.stddef) - alias c_long ssize_t; - alias slong_t time_t; - alias uint uid_t; -} else { static assert(false, "Unsupported platform"); @@ -390,7 +298,7 @@ suseconds_t useconds_t */ -version (CRuntime_Glibc) +version (linux) { static if ( __USE_FILE_OFFSET64 ) { @@ -483,53 +391,6 @@ else version (Solaris) alias id_t zoneid_t; alias id_t ctid_t; } -else version (CRuntime_Bionic) -{ - alias c_ulong fsblkcnt_t; - alias c_ulong fsfilcnt_t; - alias c_long clock_t; - alias uint id_t; - alias int key_t; - alias c_long suseconds_t; - alias uint useconds_t; // Updated in Lollipop -} -else version (CRuntime_Musl) -{ - static if ( __USE_FILE_OFFSET64 ) - { - alias ulong fsblkcnt_t; - alias ulong fsfilcnt_t; - } - else - { - alias ulong_t fsblkcnt_t; - alias ulong_t fsfilcnt_t; - } - alias uint mode_t; - alias uint id_t; - version (D_X32) - alias long susseconds_t; - else - alias c_long suseconds_t; -} -else version (CRuntime_UClibc) -{ - static if ( __USE_FILE_OFFSET64 ) - { - alias ulong fsblkcnt_t; - alias ulong fsfilcnt_t; - } - else - { - alias ulong_t fsblkcnt_t; - alias ulong_t fsfilcnt_t; - } - alias slong_t clock_t; - alias uint id_t; - alias int key_t; - alias slong_t suseconds_t; - alias uint useconds_t; -} else { static assert(false, "Unsupported platform"); @@ -896,6 +757,8 @@ else version (CRuntime_Musl) } alias int pthread_once_t; + + alias c_ulong pthread_t; } else version (Darwin) { diff --git a/libphobos/libdruntime/core/sys/posix/sys/wait.d b/libphobos/libdruntime/core/sys/posix/sys/wait.d index dada64f5e1a..91b9c9e8409 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/wait.d +++ b/libphobos/libdruntime/core/sys/posix/sys/wait.d @@ -38,7 +38,81 @@ extern (C) nothrow @nogc: /* WNOHANG WUNTRACED +*/ + +version (linux) +{ + enum WNOHANG = 1; + enum WUNTRACED = 2; + + private + { + enum __W_CONTINUED = 0xFFFF; + } +} +else version (Darwin) +{ + enum WNOHANG = 1; + enum WUNTRACED = 2; + + private + { + enum _WSTOPPED = 0x7F; // octal 0177 + } +} +else version (FreeBSD) +{ + enum WNOHANG = 1; + enum WUNTRACED = 2; + + private + { + enum _WSTOPPED = 0x7F; // octal 0177 + enum __W_CONTINUED = 0x13; + } +} +else version (NetBSD) +{ + enum WNOHANG = 1; + enum WUNTRACED = 2; + + private + { + enum _WSTOPPED = 0x7F; // octal 0177 + } +} +else version (OpenBSD) +{ + enum WNOHANG = 1; + enum WUNTRACED = 2; + + private + { + enum _WSTOPPED = 0x7F; // octal 0177 + enum _WCONTINUED = 0xFFFF; // octal 0177777 + } +} +else version (DragonFlyBSD) +{ + enum WNOHANG = 1; + enum WUNTRACED = 2; + private + { + enum _WSTOPPED = 0x7F; // octal 0177 + } +} +else version (Solaris) +{ + enum WNOHANG = 64; + enum WUNTRACED = 4; +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* WEXITSTATUS WIFCONTINUED WIFEXITED @@ -46,22 +120,14 @@ WIFSIGNALED WIFSTOPPED WSTOPSIG WTERMSIG - -pid_t wait(int*); -pid_t waitpid(pid_t, int*, int); */ version (CRuntime_Glibc) { @safe pure: - enum WNOHANG = 1; - enum WUNTRACED = 2; - private { - enum __W_CONTINUED = 0xFFFF; - extern (D) int __WTERMSIG( int status ) { return status & 0x7F; } } @@ -85,14 +151,6 @@ else version (Darwin) { @safe pure: - enum WNOHANG = 1; - enum WUNTRACED = 2; - - private - { - enum _WSTOPPED = 0x7F; // octal 0177 - } - extern (D) int _WSTATUS(int status) { return (status & 0x7F); } extern (D) int WEXITSTATUS( int status ) { return (status >> 8); } extern (D) int WIFCONTINUED( int status ) { return status == 0x13; } @@ -109,14 +167,6 @@ else version (FreeBSD) { @safe pure: - enum WNOHANG = 1; - enum WUNTRACED = 2; - - private - { - enum _WSTOPPED = 0x7F; // octal 0177 - } - extern (D) int _WSTATUS(int status) { return (status & 0x7F); } extern (D) int WEXITSTATUS( int status ) { return (status >> 8); } extern (D) int WIFCONTINUED( int status ) { return status == 0x13; } @@ -133,14 +183,6 @@ else version (NetBSD) { @safe pure: - enum WNOHANG = 1; - enum WUNTRACED = 2; - - private - { - enum _WSTOPPED = 0x7F; // octal 0177 - } - extern (D) int _WSTATUS(int status) { return (status & 0x7F); } extern (D) int WEXITSTATUS( int status ) { return (status >> 8); } extern (D) int WIFCONTINUED( int status ) { return status == 0x13; } @@ -157,15 +199,6 @@ else version (OpenBSD) { @safe pure: - enum WNOHANG = 1; - enum WUNTRACED = 2; - - private - { - enum _WSTOPPED = 0x7F; // octal 0177 - enum _WCONTINUED = 0xFFFF; // octal 0177777 - } - extern (D) int _WSTATUS(int status) { return (status & 0x7F); } extern (D) int WEXITSTATUS(int status) { return (status >> 8) & 0xFF; } extern (D) int WIFCONTINUED(int status) { return (status & _WCONTINUED) == _WCONTINUED; } @@ -182,14 +215,6 @@ else version (DragonFlyBSD) { @safe pure: - enum WNOHANG = 1; - enum WUNTRACED = 2; - - private - { - enum _WSTOPPED = 0x7F; // octal 0177 - } - extern (D) int _WSTATUS(int status) { return (status & 0x7F); } extern (D) int WEXITSTATUS( int status ) { return (status >> 8); } extern (D) int WIFCONTINUED( int status ) { return status == 0x13; } @@ -206,9 +231,6 @@ else version (Solaris) { @safe pure: - enum WNOHANG = 64; - enum WUNTRACED = 4; - extern (D) int WEXITSTATUS(int status) { return (status >> 8) & 0xff; } extern (D) int WIFCONTINUED(int status) { return (status & 0xffff) == 0xffff; } extern (D) bool WIFEXITED(int status) { return (status & 0xff) == 0; } @@ -220,10 +242,6 @@ else version (Solaris) else version (CRuntime_Bionic) { @safe pure: - - enum WNOHANG = 1; - enum WUNTRACED = 2; - extern (D) int WEXITSTATUS( int status ) { return ( status & 0xFF00 ) >> 8; } extern (D) bool WIFEXITED( int status ) { return WTERMSIG(status) == 0; } extern (D) bool WIFSIGNALED( int status ) { return WTERMSIG(status + 1) >= 2; } @@ -234,10 +252,6 @@ else version (CRuntime_Bionic) else version (CRuntime_Musl) { @safe pure: - - enum WNOHANG = 1; - enum WUNTRACED = 2; - extern (D) int WEXITSTATUS( int status ) { return ( status & 0xFF00 ) >> 8; } extern (D) int WIFCONTINUED( int status ) { return status == 0xffff; } extern (D) bool WIFEXITED( int status ) { return WTERMSIG( status ) == 0; } @@ -250,13 +264,8 @@ else version (CRuntime_UClibc) { @safe pure: - enum WNOHANG = 1; - enum WUNTRACED = 2; - private { - enum __W_CONTINUED = 0xFFFF; - extern (D) int __WTERMSIG( int status ) { return status & 0x7F; } } @@ -288,6 +297,11 @@ else static assert(false, "Unsupported platform"); } +/* +pid_t wait(int*); +pid_t waitpid(pid_t, int*, int); +*/ + pid_t wait(int*); pid_t waitpid(pid_t, int*, int); @@ -306,11 +320,9 @@ enum idtype_t P_PID, P_PGID } - -int waitid(idtype_t, id_t, siginfo_t*, int); */ -version (CRuntime_Glibc) +version (linux) { enum WEXITED = 4; enum WSTOPPED = 2; @@ -323,8 +335,6 @@ version (CRuntime_Glibc) P_PID, P_PGID } - - int waitid(idtype_t, id_t, siginfo_t*, int); } else version (Darwin) { @@ -339,8 +349,6 @@ else version (Darwin) P_PID, P_PGID } - - int waitid(idtype_t, id_t, siginfo_t*, int); } else version (FreeBSD) { @@ -369,8 +377,6 @@ else version (FreeBSD) P_CPUID, P_PSETID } - - int waitid(idtype_t, id_t, siginfo_t*, int); } else version (NetBSD) { @@ -419,50 +425,51 @@ else version (Solaris) P_CPUID, /* CPU identifier. */ P_PSETID, /* Processor set identifier */ } +} +else +{ + static assert(false, "Unsupported platform"); +} + +/* +int waitid(idtype_t, id_t, siginfo_t*, int); +*/ +version (CRuntime_Glibc) +{ + int waitid(idtype_t, id_t, siginfo_t*, int); +} +else version (Darwin) +{ + int waitid(idtype_t, id_t, siginfo_t*, int); +} +else version (FreeBSD) +{ + int waitid(idtype_t, id_t, siginfo_t*, int); +} +else version (NetBSD) +{ +} +else version (OpenBSD) +{ +} +else version (DragonFlyBSD) +{ +} +else version (Solaris) +{ int waitid(idtype_t, id_t, siginfo_t*, int); } else version (CRuntime_Bionic) { - enum WEXITED = 4; - enum WSTOPPED = 2; - enum WCONTINUED = 8; - enum WNOWAIT = 0x01000000; - - alias int idtype_t; - int waitid(idtype_t, id_t, siginfo_t*, int); } else version (CRuntime_Musl) { - enum WEXITED = 4; - enum WSTOPPED = 2; - enum WCONTINUED = 8; - enum WNOWAIT = 0x01000000; - - enum idtype_t - { - P_ALL, - P_PID, - P_PGID - } - int waitid(idtype_t, id_t, siginfo_t*, int); } else version (CRuntime_UClibc) { - enum WEXITED = 4; - enum WSTOPPED = 2; - enum WCONTINUED = 8; - enum WNOWAIT = 0x01000000; - - enum idtype_t - { - P_ALL, - P_PID, - P_PGID - } - int waitid(idtype_t, id_t, siginfo_t*, int); } else diff --git a/libphobos/libdruntime/core/sys/posix/termios.d b/libphobos/libdruntime/core/sys/posix/termios.d index 5de678506d9..357060bd57f 100644 --- a/libphobos/libdruntime/core/sys/posix/termios.d +++ b/libphobos/libdruntime/core/sys/posix/termios.d @@ -129,20 +129,9 @@ TCIOFF TCION TCOOFF TCOON - -speed_t cfgetispeed(const scope termios*); -speed_t cfgetospeed(const scope termios*); -int cfsetispeed(termios*, speed_t); -int cfsetospeed(termios*, speed_t); -int tcdrain(int); -int tcflow(int, int); -int tcflush(int, int); -int tcgetattr(int, termios*); -int tcsendbreak(int, int); -int tcsetattr(int, int, const scope termios*); */ -version (CRuntime_Glibc) +version (linux) { alias ubyte cc_t; alias uint speed_t; @@ -239,17 +228,6 @@ version (CRuntime_Glibc) enum TCION = 3; enum TCOOFF = 0; enum TCOON = 1; - - speed_t cfgetispeed(const scope termios*); - speed_t cfgetospeed(const scope termios*); - int cfsetispeed(termios*, speed_t); - int cfsetospeed(termios*, speed_t); - int tcdrain(int); - int tcflow(int, int); - int tcflush(int, int); - int tcgetattr(int, termios*); - int tcsendbreak(int, int); - int tcsetattr(int, int, const scope termios*); } else version (Darwin) { @@ -347,18 +325,6 @@ else version (Darwin) enum TCION = 4; enum TCOOFF = 1; enum TCOON = 2; - - speed_t cfgetispeed(const scope termios*); - speed_t cfgetospeed(const scope termios*); - int cfsetispeed(termios*, speed_t); - int cfsetospeed(termios*, speed_t); - int tcdrain(int); - int tcflow(int, int); - int tcflush(int, int); - int tcgetattr(int, termios*); - int tcsendbreak(int, int); - int tcsetattr(int, int, const scope termios*); - } else version (FreeBSD) { @@ -456,17 +422,6 @@ else version (FreeBSD) enum TCION = 4; enum TCOOFF = 1; enum TCOON = 2; - - speed_t cfgetispeed(const scope termios*); - speed_t cfgetospeed(const scope termios*); - int cfsetispeed(termios*, speed_t); - int cfsetospeed(termios*, speed_t); - int tcdrain(int); - int tcflow(int, int); - int tcflush(int, int); - int tcgetattr(int, termios*); - int tcsendbreak(int, int); - int tcsetattr(int, int, const scope termios*); } else version (DragonFlyBSD) { @@ -564,17 +519,6 @@ else version (DragonFlyBSD) enum TCION = 4; enum TCOOFF = 1; enum TCOON = 2; - - speed_t cfgetispeed(const scope termios*); - speed_t cfgetospeed(const scope termios*); - int cfsetispeed(termios*, speed_t); - int cfsetospeed(termios*, speed_t); - int tcdrain(int); - int tcflow(int, int); - int tcflush(int, int); - int tcgetattr(int, termios*); - int tcsendbreak(int, int); - int tcsetattr(int, int, const scope termios*); } else version (NetBSD) { @@ -672,17 +616,6 @@ else version (NetBSD) enum TCION = 4; enum TCOOFF = 1; enum TCOON = 2; - - speed_t cfgetispeed(const scope termios*); - speed_t cfgetospeed(const scope termios*); - int cfsetispeed(termios*, speed_t); - int cfsetospeed(termios*, speed_t); - int tcdrain(int); - int tcflow(int, int); - int tcflush(int, int); - int tcgetattr(int, termios*); - int tcsendbreak(int, int); - int tcsetattr(int, int, const scope termios*); } else version (OpenBSD) { @@ -780,17 +713,6 @@ else version (OpenBSD) enum TCION = 4; enum TCOOFF = 1; enum TCOON = 2; - - speed_t cfgetispeed(const scope termios*); - speed_t cfgetospeed(const scope termios*); - int cfsetispeed(termios*, speed_t); - int cfsetospeed(termios*, speed_t); - int tcdrain(int); - int tcflow(int, int); - int tcflush(int, int); - int tcgetattr(int, termios*); - int tcsendbreak(int, int); - int tcsetattr(int, int, const scope termios*); } else version (Solaris) { @@ -909,11 +831,102 @@ else version (Solaris) enum B307200 = 21; enum B460800 = 22; enum B921600 = 23; +} + +/* +speed_t cfgetispeed(const scope termios*); +speed_t cfgetospeed(const scope termios*); +int cfsetispeed(termios*, speed_t); +int cfsetospeed(termios*, speed_t); +int tcdrain(int); +int tcflow(int, int); +int tcflush(int, int); +int tcgetattr(int, termios*); +int tcsendbreak(int, int); +int tcsetattr(int, int, const scope termios*); +*/ - /* - * POSIX termios functions - * These functions get mapped into ioctls. - */ +version (CRuntime_Glibc) +{ + speed_t cfgetispeed(const scope termios*); + speed_t cfgetospeed(const scope termios*); + int cfsetispeed(termios*, speed_t); + int cfsetospeed(termios*, speed_t); + int tcdrain(int); + int tcflow(int, int); + int tcflush(int, int); + int tcgetattr(int, termios*); + int tcsendbreak(int, int); + int tcsetattr(int, int, const scope termios*); +} +else version (Darwin) +{ + speed_t cfgetispeed(const scope termios*); + speed_t cfgetospeed(const scope termios*); + int cfsetispeed(termios*, speed_t); + int cfsetospeed(termios*, speed_t); + int tcdrain(int); + int tcflow(int, int); + int tcflush(int, int); + int tcgetattr(int, termios*); + int tcsendbreak(int, int); + int tcsetattr(int, int, const scope termios*); + +} +else version (FreeBSD) +{ + speed_t cfgetispeed(const scope termios*); + speed_t cfgetospeed(const scope termios*); + int cfsetispeed(termios*, speed_t); + int cfsetospeed(termios*, speed_t); + int tcdrain(int); + int tcflow(int, int); + int tcflush(int, int); + int tcgetattr(int, termios*); + int tcsendbreak(int, int); + int tcsetattr(int, int, const scope termios*); +} +else version (DragonFlyBSD) +{ + speed_t cfgetispeed(const scope termios*); + speed_t cfgetospeed(const scope termios*); + int cfsetispeed(termios*, speed_t); + int cfsetospeed(termios*, speed_t); + int tcdrain(int); + int tcflow(int, int); + int tcflush(int, int); + int tcgetattr(int, termios*); + int tcsendbreak(int, int); + int tcsetattr(int, int, const scope termios*); +} +else version (NetBSD) +{ + speed_t cfgetispeed(const scope termios*); + speed_t cfgetospeed(const scope termios*); + int cfsetispeed(termios*, speed_t); + int cfsetospeed(termios*, speed_t); + int tcdrain(int); + int tcflow(int, int); + int tcflush(int, int); + int tcgetattr(int, termios*); + int tcsendbreak(int, int); + int tcsetattr(int, int, const scope termios*); +} +else version (OpenBSD) +{ + speed_t cfgetispeed(const scope termios*); + speed_t cfgetospeed(const scope termios*); + int cfsetispeed(termios*, speed_t); + int cfsetospeed(termios*, speed_t); + int tcdrain(int); + int tcflow(int, int); + int tcflush(int, int); + int tcgetattr(int, termios*); + int tcsendbreak(int, int); + int tcsetattr(int, int, const scope termios*); +} +else version (Solaris) +{ speed_t cfgetospeed(const scope termios*); int cfsetospeed(termios*, speed_t); speed_t cfgetispeed(const scope termios*); @@ -927,108 +940,6 @@ else version (Solaris) } else version (CRuntime_UClibc) { - alias ubyte cc_t; - alias uint speed_t; - alias uint tcflag_t; - - enum NCCS = 32; - - struct termios - { - tcflag_t c_iflag; - tcflag_t c_oflag; - tcflag_t c_cflag; - tcflag_t c_lflag; - cc_t c_line; - cc_t[NCCS] c_cc; - speed_t c_ispeed; - speed_t c_ospeed; - } - - enum VINTR = 0; - enum VQUIT = 1; - enum VERASE = 2; - enum VKILL = 3; - enum VEOF = 4; - enum VTIME = 5; - enum VMIN = 6; - enum VSWTC = 7; - enum VSTART = 8; - enum VSTOP = 9; - enum VSUSP = 10; - enum VEOL = 11; - enum VREPRINT = 12; - enum VDISCARD = 13; - enum VWERASE = 14; - enum VLNEXT = 15; - enum VEOL2 = 16; - - enum BRKINT = 0x0000002; // 0000002 - enum ICRNL = 0x0000100; // 0000400 - enum IGNBRK = 0x0000001; // 0000001 - enum IGNCR = 0x0000080; // 0000200 - enum IGNPAR = 0x0000004; // 0000004 - enum INLCR = 0x0000040; // 0000100 - enum INPCK = 0x0000010; // 0000020 - enum ISTRIP = 0x0000020; // 0000040 - enum IXOFF = 0x0001000; // 0010000 - enum IXON = 0x0000400; // 0002000 - enum PARMRK = 0x0000008; // 0000010 - - enum OPOST = 0x0000001; // 0000001 - - enum B0 = 0x0000000; // 0000000 - enum B50 = 0x0000001; // 0000001 - enum B75 = 0x0000002; // 0000002 - enum B110 = 0x0000003; // 0000003 - enum B134 = 0x0000004; // 0000004 - enum B150 = 0x0000005; // 0000005 - enum B200 = 0x0000006; // 0000006 - enum B300 = 0x0000007; // 0000007 - enum B600 = 0x0000008; // 0000010 - enum B1200 = 0x0000009; // 0000011 - enum B1800 = 0x000000A; // 0000012 - enum B2400 = 0x000000B; // 0000013 - enum B4800 = 0x000000C; // 0000014 - enum B9600 = 0x000000D; // 0000015 - enum B19200 = 0x000000E; // 0000016 - enum B38400 = 0x000000F; // 0000017 - - enum CSIZE = 0x0000030; // 0000060 - enum CS5 = 0x0000000; // 0000000 - enum CS6 = 0x0000010; // 0000020 - enum CS7 = 0x0000020; // 0000040 - enum CS8 = 0x0000030; // 0000060 - enum CSTOPB = 0x0000040; // 0000100 - enum CREAD = 0x0000080; // 0000200 - enum PARENB = 0x0000100; // 0000400 - enum PARODD = 0x0000200; // 0001000 - enum HUPCL = 0x0000400; // 0002000 - enum CLOCAL = 0x0000800; // 0004000 - - enum ECHO = 0x0000008; // 0000010 - enum ECHOE = 0x0000010; // 0000020 - enum ECHOK = 0x0000020; // 0000040 - enum ECHONL = 0x0000040; // 0000100 - enum ICANON = 0x0000002; // 0000002 - enum IEXTEN = 0x0008000; // 0100000 - enum ISIG = 0x0000001; // 0000001 - enum NOFLSH = 0x0000080; // 0000200 - enum TOSTOP = 0x0000100; // 0000400 - - enum TCSANOW = 0; - enum TCSADRAIN = 1; - enum TCSAFLUSH = 2; - - enum TCIFLUSH = 0; - enum TCOFLUSH = 1; - enum TCIOFLUSH = 2; - - enum TCIOFF = 2; - enum TCION = 3; - enum TCOOFF = 0; - enum TCOON = 1; - speed_t cfgetispeed(const scope termios*); speed_t cfgetospeed(const scope termios*); int cfsetispeed(termios*, speed_t); diff --git a/libphobos/libdruntime/core/sys/posix/ucontext.d b/libphobos/libdruntime/core/sys/posix/ucontext.d index e38aa96d8eb..20297f50b90 100644 --- a/libphobos/libdruntime/core/sys/posix/ucontext.d +++ b/libphobos/libdruntime/core/sys/posix/ucontext.d @@ -63,9 +63,8 @@ struct ucontext_t } */ -version (CRuntime_Glibc) +version (linux) { - version (X86_64) { enum @@ -146,7 +145,8 @@ version (CRuntime_Glibc) mcontext_t uc_mcontext; sigset_t uc_sigmask; _libc_fpstate __fpregs_mem; - ulong[4] __ssp; + version (CRuntime_Glibc) + ulong[4] __ssp; } } else version (X86) @@ -218,7 +218,8 @@ version (CRuntime_Glibc) mcontext_t uc_mcontext; sigset_t uc_sigmask; _libc_fpstate __fpregs_mem; - c_ulong[4] __ssp; + version (CRuntime_Glibc) + c_ulong[4] __ssp; } } else version (HPPA) @@ -788,139 +789,6 @@ version (CRuntime_Glibc) else static assert(0, "unimplemented"); } -else version (CRuntime_Musl) -{ - version (AArch64) - { - struct mcontext_t - { - real[18+256] __regs; - } - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - sigset_t uc_sigmask; - mcontext_t uc_mcontext; - } - } - else version (ARM) - { - struct mcontext_t - { - c_ulong[21] __regs; - } - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - ulong[64] uc_regspace; - } - } - else version (IBMZ_Any) - { - struct mcontext_t - { - c_ulong[18] __regs1; - uint[18] __regs2; - double[16] __regs3; - } - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - } - } - else version (MIPS_Any) - { - version (MIPS_N32) - { - struct mcontext_t - { - ulong[32] __mc1; - double[32] __mc2; - ulong[9] __mc3; - uint[4] __mc4; - } - } - else version (MIPS64) - { - struct mcontext_t - { - ulong[32] __mc1; - double[32] __mc2; - ulong[9] __mc3; - uint[4] __mc4; - } - } - else - { - struct mcontext_t - { - uint[2] __mc1; - ulong[65] __mc2; - uint[5] __mc3; - ulong[2] __mc4; - uint[6] __mc5; - } - } - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - } - } - else version (X86) - { - struct mcontext_t - { - uint[22] __space; - } - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - c_ulong[28] __fpregs_mem; - } - } - else version (X86_64) - { - struct mcontext_t - { - ulong[32] __space; - } - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - ulong[64] __fpregs_mem; - } - } - else - static assert(0, "unimplemented"); -} else version (Darwin) { private @@ -1722,186 +1590,6 @@ else version (Solaris) } } } -else version (CRuntime_UClibc) -{ - version (X86_64) - { - enum - { - REG_R8 = 0, - REG_R9, - REG_R10, - REG_R11, - REG_R12, - REG_R13, - REG_R14, - REG_R15, - REG_RDI, - REG_RSI, - REG_RBP, - REG_RBX, - REG_RDX, - REG_RAX, - REG_RCX, - REG_RSP, - REG_RIP, - REG_EFL, - REG_CSGSFS, /* Actually short cs, gs, fs, __pad0. */ - REG_ERR, - REG_TRAPNO, - REG_OLDMASK, - REG_CR2 - } - - alias sigcontext mcontext_t; - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - } - } - else version (MIPS32) - { - alias greg_t = ulong; - enum NGREG = 32; - enum NFPREG = 32; - alias gregset_t = greg_t[NGREG]; - - struct fpregset_t - { - union fp_r - { - double[NFPREG] fp_dregs; - struct _fp_fregs - { - float _fp_fregs; - uint _fp_pad; - } - _fp_fregs[NFPREG] fp_fregs; - } - } - - version (MIPS_O32) - { - struct mcontext_t - { - uint regmask; - uint status; - greg_t pc; - gregset_t gregs; - fpregset_t fpregs; - uint fp_owned; - uint fpc_csr; - uint fpc_eir; - uint used_math; - uint dsp; - greg_t mdhi; - greg_t mdlo; - c_ulong hi1; - c_ulong lo1; - c_ulong hi2; - c_ulong lo2; - c_ulong hi3; - c_ulong lo3; - } - } - else - { - struct mcontext_t - { - gregset_t gregs; - fpregset_t fpregs; - greg_t mdhi; - greg_t hi1; - greg_t hi2; - greg_t hi3; - greg_t mdlo; - greg_t lo1; - greg_t lo2; - greg_t lo3; - greg_t pc; - uint fpc_csr; - uint used_math; - uint dsp; - uint reserved; - } - } - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - } - } - else version (ARM) - { - enum - { - R0 = 0, - R1 = 1, - R2 = 2, - R3 = 3, - R4 = 4, - R5 = 5, - R6 = 6, - R7 = 7, - R8 = 8, - R9 = 9, - R10 = 10, - R11 = 11, - R12 = 12, - R13 = 13, - R14 = 14, - R15 = 15 - } - - struct sigcontext - { - c_ulong trap_no; - c_ulong error_code; - c_ulong oldmask; - c_ulong arm_r0; - c_ulong arm_r1; - c_ulong arm_r2; - c_ulong arm_r3; - c_ulong arm_r4; - c_ulong arm_r5; - c_ulong arm_r6; - c_ulong arm_r7; - c_ulong arm_r8; - c_ulong arm_r9; - c_ulong arm_r10; - c_ulong arm_fp; - c_ulong arm_ip; - c_ulong arm_sp; - c_ulong arm_lr; - c_ulong arm_pc; - c_ulong arm_cpsr; - c_ulong fault_address; - } - - alias sigcontext mcontext_t; - - struct ucontext_t - { - c_ulong uc_flags; - ucontext_t* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - sigset_t uc_sigmask; - align(8) c_ulong[128] uc_regspace; - } - } - else - static assert(0, "unimplemented"); -} // // Obsolescent (OB) diff --git a/libphobos/libdruntime/core/sys/solaris/dlfcn.d b/libphobos/libdruntime/core/sys/solaris/dlfcn.d index 4f69bfdbcec..343f003b50d 100644 --- a/libphobos/libdruntime/core/sys/solaris/dlfcn.d +++ b/libphobos/libdruntime/core/sys/solaris/dlfcn.d @@ -14,21 +14,6 @@ nothrow: public import core.sys.posix.dlfcn; import core.stdc.config; -// enum RTLD_LAZY = 0x00001; // POSIX -// enum RTLD_NOW = 0x00002; // POSIX -enum RTLD_NOLOAD = 0x00004; -enum RTLD_DEEPBIND = 0x00008; - -// enum RTLD_GLOBAL = 0x00100; // POSIX -// enum RTLD_LOCAL = 0; // POSIX -enum RTLD_PARENT = 0x00200; -enum RTLD_GROUP = 0x00400; -enum RTLD_WORLD = 0x00800; -enum RTLD_NODELETE = 0x01000; -enum RTLD_FIRST = 0x02000; -enum RTLD_CONFGEN = 0x10000; - - enum { RTLD_NEXT = cast(void *)-1, @@ -58,21 +43,12 @@ enum int dldump(const scope char*, const scope char*, int); -struct Dl_info -{ - const(char)* dli_fname; - void* dli_fbase; - const(char)* dli_sname; - void* dli_saddr; -} - enum { RTLD_DL_SYMENT = 1, RTLD_DL_LINKMAP = 2, } -int dladdr(const(void)*, Dl_info*); int dladdr1(void*, Dl_info*, void**, int); enum diff --git a/libphobos/libdruntime/core/sys/windows/wingdi.d b/libphobos/libdruntime/core/sys/windows/wingdi.d index 279f6be627b..ba45c27d359 100644 --- a/libphobos/libdruntime/core/sys/windows/wingdi.d +++ b/libphobos/libdruntime/core/sys/windows/wingdi.d @@ -4191,8 +4191,8 @@ extern(Windows) nothrow @nogc { DWORD GetGlyphOutlineA(HDC, UINT, UINT, LPGLYPHMETRICS, DWORD, PVOID, const(MAT2)*); DWORD GetGlyphOutlineW(HDC, UINT, UINT, LPGLYPHMETRICS, DWORD, PVOID, const(MAT2)*); int GetGraphicsMode(HDC); - BOOL GetICMProfileA(HDC, DWORD, LPSTR); - BOOL GetICMProfileW(HDC, DWORD, LPWSTR); + BOOL GetICMProfileA(HDC, LPDWORD, LPSTR); + BOOL GetICMProfileW(HDC, LPDWORD, LPWSTR); DWORD GetKerningPairsA(HDC, DWORD, LPKERNINGPAIR); DWORD GetKerningPairsW(HDC, DWORD, LPKERNINGPAIR); BOOL GetLogColorSpaceA(HCOLORSPACE, LPLOGCOLORSPACEA, DWORD); diff --git a/libphobos/libdruntime/core/thread/osthread.d b/libphobos/libdruntime/core/thread/osthread.d index fe4d24fafce..ca368098b4b 100644 --- a/libphobos/libdruntime/core/thread/osthread.d +++ b/libphobos/libdruntime/core/thread/osthread.d @@ -247,15 +247,6 @@ else class Thread : ThreadBase { // - // Main process thread - // - version (FreeBSD) - { - // set when suspend failed and should be retried, see Issue 13416 - private shared bool m_suspendagain; - } - - // // Standard thread data // version (Windows) @@ -2019,7 +2010,6 @@ extern (C) void thread_suspendAll() nothrow // subtract own thread assert(cnt >= 1); --cnt; - Lagain: // wait for semaphore notifications for (; cnt; --cnt) { @@ -2030,20 +2020,6 @@ extern (C) void thread_suspendAll() nothrow errno = 0; } } - version (FreeBSD) - { - // avoid deadlocks, see Issue 13416 - t = ThreadBase.sm_tbeg.toThread; - while (t) - { - auto tn = t.next; - if (t.m_suspendagain && suspend(t)) - ++cnt; - t = tn.toThread; - } - if (cnt) - goto Lagain; - } } } } @@ -2480,7 +2456,6 @@ else version (Posix) status = sigdelset( &sigres, resumeSignalNumber ); assert( status == 0 ); - version (FreeBSD) obj.m_suspendagain = false; status = sem_post( &suspendCount ); assert( status == 0 ); @@ -2491,19 +2466,6 @@ else version (Posix) obj.m_curr.tstack = obj.m_curr.bstack; } } - - // avoid deadlocks on FreeBSD, see Issue 13416 - version (FreeBSD) - { - auto obj = Thread.getThis(); - if (THR_IN_CRITICAL(obj.m_addr)) - { - obj.m_suspendagain = true; - if (sem_post(&suspendCount)) assert(0); - return; - } - } - callWithStackShell(&op); } @@ -2517,29 +2479,6 @@ else version (Posix) { } - - // HACK libthr internal (thr_private.h) macro, used to - // avoid deadlocks in signal handler, see Issue 13416 - version (FreeBSD) bool THR_IN_CRITICAL(pthread_t p) nothrow @nogc - { - import core.sys.posix.config : c_long; - import core.sys.posix.sys.types : lwpid_t; - - // If the begin of pthread would be changed in libthr (unlikely) - // we'll run into undefined behavior, compare with thr_private.h. - static struct pthread - { - c_long tid; - static struct umutex { lwpid_t owner; uint flags; uint[2] ceilings; uint[4] spare; } - umutex lock; - uint cycle; - int locklevel; - int critical_count; - // ... - } - auto priv = cast(pthread*)p; - return priv.locklevel > 0 || priv.critical_count > 0; - } } } else diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index c989caa64bf..7bb6bec7ed9 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -180,7 +180,30 @@ class Object /** * Test whether $(D this) is equal to $(D o). * The default implementation only compares by identity (using the $(D is) operator). - * Generally, overrides for $(D opEquals) should attempt to compare objects by their contents. + * Generally, overrides and overloads for $(D opEquals) should attempt to compare objects by their contents. + * A class will most likely want to add an overload that takes your specific type as the argument + * and does the content comparison. Then you can override this and forward it to your specific + * typed overload with a cast. Remember to check for `null` on the typed overload. + * + * Examples: + * --- + * class Child { + * int contents; + * // the typed overload first. It can use all the attribute you want + * bool opEquals(const Child c) const @safe pure nothrow @nogc + * { + * if (c is null) + * return false; + * return this.contents == c.contents; + * } + * + * // and now the generic override forwards with a cast + * override bool opEquals(Object o) + * { + * return this.opEquals(cast(Child) o); + * } + * } + * --- */ bool opEquals(Object o) { @@ -237,41 +260,50 @@ class Object } } -bool opEquals(Object lhs, Object rhs) +/++ + Implementation for class opEquals override. Calls the class-defined methods after a null check. + Please note this is not nogc right now, even if your implementation is, because of + the typeinfo name string compare. This is because of dmd's dll implementation. However, + it can infer to @safe if your class' opEquals is. ++/ +bool opEquals(LHS, RHS)(LHS lhs, RHS rhs) if (is(LHS : const Object) && is(RHS : const Object)) { - // If aliased to the same object or both null => equal - if (lhs is rhs) return true; + static if (__traits(compiles, lhs.opEquals(rhs)) && __traits(compiles, rhs.opEquals(lhs))) + { + // If aliased to the same object or both null => equal + if (lhs is rhs) return true; - // If either is null => non-equal - if (lhs is null || rhs is null) return false; + // If either is null => non-equal + if (lhs is null || rhs is null) return false; - if (!lhs.opEquals(rhs)) return false; + if (!lhs.opEquals(rhs)) return false; - // If same exact type => one call to method opEquals - if (typeid(lhs) is typeid(rhs) || - !__ctfe && typeid(lhs).opEquals(typeid(rhs))) - /* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't - (issue 7147). But CTFE also guarantees that equal TypeInfos are - always identical. So, no opEquals needed during CTFE. */ - { - return true; - } + // If same exact type => one call to method opEquals + if (typeid(lhs) is typeid(rhs) || + !__ctfe && typeid(lhs).opEquals(typeid(rhs))) + /* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't + (issue 7147). But CTFE also guarantees that equal TypeInfos are + always identical. So, no opEquals needed during CTFE. */ + { + return true; + } - // General case => symmetric calls to method opEquals - return rhs.opEquals(lhs); -} + // General case => symmetric calls to method opEquals + return rhs.opEquals(lhs); + } + else + { + // this is a compatibility hack for the old const cast behavior + // if none of the new overloads compile, we'll go back plain Object, + // including casting away const. It does this through the pointer + // to bypass any opCast that may be present on the original class. + return .opEquals!(Object, Object)(*cast(Object*) &lhs, *cast(Object*) &rhs); -/************************ -* Returns true if lhs and rhs are equal. -*/ -bool opEquals(const Object lhs, const Object rhs) -{ - // A hack for the moment. - return opEquals(cast()lhs, cast()rhs); + } } /// If aliased to the same object or both null => equal -@system unittest +@system unittest // this one is not @safe because it goes through the Object base method { class F { int flag; this(int flag) { this.flag = flag; } } @@ -291,7 +323,8 @@ bool opEquals(const Object lhs, const Object rhs) } /// If same exact type => one call to method opEquals -@system unittest +/// This test passes `@safe` because it defines a new opEquals with `@safe` +@safe unittest { class F { @@ -302,9 +335,9 @@ bool opEquals(const Object lhs, const Object rhs) this.flag = flag; } - override bool opEquals(const Object o) + bool opEquals(const F o) const @safe nothrow pure { - return flag == (cast(F) o).flag; + return flag == o.flag; } } @@ -314,7 +347,7 @@ bool opEquals(const Object lhs, const Object rhs) } /// General case => symmetric calls to method opEquals -@system unittest +@safe unittest { int fEquals, gEquals; @@ -331,10 +364,10 @@ bool opEquals(const Object lhs, const Object rhs) { this(int flag) { super(flag); } - override bool opEquals(const Object o) + bool opEquals(const Base o) @safe { fEquals++; - return flag == (cast(Base) o).flag; + return flag == o.flag; } } @@ -342,10 +375,10 @@ bool opEquals(const Object lhs, const Object rhs) { this(int flag) { super(flag); } - override bool opEquals(const Object o) + bool opEquals(const Base o) @safe { gEquals++; - return flag == (cast(Base) o).flag; + return flag == o.flag; } } @@ -354,6 +387,114 @@ bool opEquals(const Object lhs, const Object rhs) assert(gEquals == 1); } +/++ + This test shows an example for a comprehensive inheritance equality chain too. ++/ +unittest +{ + static class Base + { + int member; + + this(int member) pure @safe nothrow @nogc + { + this.member = member; + } + + override bool opEquals(Object rhs) const + { + return this.opEquals(cast(Base) rhs); + } + + bool opEquals(const Base rhs) const @nogc pure nothrow @safe + { + if (rhs is null) + return false; + return this.member == rhs.member; + } + } + + // works through the direct class with attributes enabled, except for pure and nogc in the current TypeInfo implementation + bool testThroughBase() nothrow @safe + { + Base b1 = new Base(0); + Base b2 = new Base(0); + assert(b1 == b2); + Base b3 = new Base(1); + assert(b1 != b3); + return true; + } + + static assert(testThroughBase()); + + // also works through the base class interface thanks to the override, but no more attributes + bool testThroughObject() + { + Object o1 = new Base(0); + Object o2 = new Base(0); + assert(o1 == o2); + Object o3 = new Base(1); + assert(o1 != o3); + return true; + } + + static assert(testThroughObject()); + + // Each time you make a child, you want to override all old opEquals + // and add a new overload for the new child. + static class Child : Base + { + int member2; + + this(int member, int member2) pure @safe nothrow @nogc + { + super(member); + this.member2 = member2; + } + + // override the whole chain so it works consistently though any base + override bool opEquals(Object rhs) const + { + return this.opEquals(cast(Child) rhs); + } + override bool opEquals(const Base rhs) const + { + return this.opEquals(cast(const Child) rhs); + } + // and then add the new overload, if necessary, to handle new members + bool opEquals(const Child rhs) const @nogc pure nothrow @safe + { + if (rhs is null) + return false; + // can call back to the devirtualized base test with implicit conversion + // then compare the new member too. or we could have just compared the base + // member directly here as well. + return Base.opEquals(rhs) && this.member2 == rhs.member2; + } + + // a mixin template, of course, could automate this. + } + + bool testThroughChild() + { + Child a = new Child(0, 0); + Child b = new Child(0, 1); + assert(a != b); + + Base ba = a; + Base bb = b; + assert(ba != bb); + + Object oa = a; + Object ob = b; + assert(oa != ob); + + return true; + } + + static assert(testThroughChild()); +} + // To cover const Object opEquals @system unittest { @@ -396,7 +537,8 @@ void setSameMutex(shared Object ownee, shared Object owner) */ struct Interface { - TypeInfo_Class classinfo; /// .classinfo for this interface (not for containing class) + /// Class info returned by `typeid` for this interface (not for containing class) + TypeInfo_Class classinfo; void*[] vtbl; size_t offset; /// offset to Interface 'this' from Object 'this' } @@ -447,13 +589,17 @@ class TypeInfo override bool opEquals(Object o) { + return opEquals(cast(TypeInfo) o); + } + + bool opEquals(const TypeInfo ti) @safe nothrow const + { /* TypeInfo instances are singletons, but duplicates can exist * across DLL's. Therefore, comparing for a name match is * sufficient. */ - if (this is o) + if (this is ti) return true; - auto ti = cast(const TypeInfo)o; return ti && this.toString() == ti.toString(); } @@ -462,7 +608,7 @@ class TypeInfo auto anotherObj = new Object(); assert(typeid(void).opEquals(typeid(void))); - assert(!typeid(void).opEquals(anotherObj)); + assert(typeid(void) != anotherObj); // calling .opEquals here directly is a type mismatch } /** @@ -1397,13 +1543,13 @@ private extern (C) int _d_isbaseof(scope TypeInfo_Class child, /** * Runtime type information about a class. * Can be retrieved from an object instance by using the - * $(DDSUBLINK spec/property,classinfo, .classinfo) property. + * $(DDSUBLINK spec/expression,typeid_expressions,typeid expression). */ class TypeInfo_Class : TypeInfo { override string toString() const pure { return name; } - override bool opEquals(Object o) + override bool opEquals(const TypeInfo o) const { if (this is o) return true; @@ -1739,12 +1885,8 @@ class TypeInfo_Struct : TypeInfo return false; else if (xopEquals) { - version (GNU) - { // BUG: GDC and DMD use different calling conventions - return (*xopEquals)(p2, p1); - } - else - return (*xopEquals)(p1, p2); + const dg = _memberFunc(p2, xopEquals); + return dg.xopEquals(p1); } else if (p1 == p2) return true; @@ -1766,12 +1908,8 @@ class TypeInfo_Struct : TypeInfo return true; else if (xopCmp) { - version (GNU) - { // BUG: GDC and DMD use different calling conventions - return (*xopCmp)(p1, p2); - } - else - return (*xopCmp)(p2, p1); + const dg = _memberFunc(p1, xopCmp); + return dg.xopCmp(p2); } else // BUG: relies on the GC not moving objects @@ -1876,6 +2014,28 @@ class TypeInfo_Struct : TypeInfo TypeInfo m_arg2; } immutable(void)* m_RTInfo; // data for precise GC + + // The xopEquals and xopCmp members are function pointers to member + // functions, which is not guaranteed to share the same ABI, as it is not + // known whether the `this` parameter is the first or second argument. + // This wrapper is to convert it to a delegate which will always pass the + // `this` parameter in the correct way. + private struct _memberFunc + { + union + { + struct // delegate + { + const void* ptr; + const void* funcptr; + } + @safe pure nothrow + { + bool delegate(in void*) xopEquals; + int delegate(in void*) xopCmp; + } + } + } } @system unittest @@ -2715,7 +2875,7 @@ void clear(Value, Key)(Value[Key] aa) _aaClear(*cast(AA *) &aa); } -/* ditto */ +/** ditto */ void clear(Value, Key)(Value[Key]* aa) { _aaClear(*cast(AA *) aa); @@ -2762,21 +2922,21 @@ T rehash(T : Value[Key], Value, Key)(T aa) return aa; } -/* ditto */ +/** ditto */ T rehash(T : Value[Key], Value, Key)(T* aa) { _aaRehash(cast(AA*)aa, typeid(Value[Key])); return *aa; } -/* ditto */ +/** ditto */ T rehash(T : shared Value[Key], Value, Key)(T aa) { _aaRehash(cast(AA*)&aa, typeid(Value[Key])); return aa; } -/* ditto */ +/** ditto */ T rehash(T : shared Value[Key], Value, Key)(T* aa) { _aaRehash(cast(AA*)aa, typeid(Value[Key])); @@ -2784,8 +2944,8 @@ T rehash(T : shared Value[Key], Value, Key)(T* aa) } /*********************************** - * Create a new associative array of the same size and copy the contents of the - * associative array into it. + * Creates a new associative array of the same size and copies the contents of + * the associative array into it. * Params: * aa = The associative array. */ @@ -2826,7 +2986,7 @@ V[K] dup(T : V[K], K, V)(T aa) return result; } -/* ditto */ +/** ditto */ V[K] dup(T : V[K], K, V)(T* aa) { return (*aa).dup; @@ -2853,11 +3013,27 @@ private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe } /*********************************** - * Returns a forward range over the keys of the associative array. + * Returns a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + * which will iterate over the keys of the associative array. The keys are + * returned by reference. + * + * If structural changes are made to the array (removing or adding keys), all + * ranges previously obtained through this function are invalidated. The + * following example program will dereference a null pointer: + * + *--- + * import std.stdio : writeln; + * + * auto dict = ["k1": 1, "k2": 2]; + * auto keyRange = dict.byKey; + * dict.clear; + * writeln(keyRange.front); // Segmentation fault + *--- + * * Params: * aa = The associative array. * Returns: - * A forward range. + * A forward range referencing the keys of the associative array. */ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { @@ -2880,7 +3056,7 @@ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe return Result(_aaToRange(aa)); } -/* ditto */ +/** ditto */ auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc { return (*aa).byKey(); @@ -2889,7 +3065,7 @@ auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc /// @safe unittest { - auto dict = [1: 0, 2: 0]; + auto dict = [1: "v1", 2: "v2"]; int sum; foreach (v; dict.byKey) sum += v; @@ -2898,11 +3074,27 @@ auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc } /*********************************** - * Returns a forward range over the values of the associative array. + * Returns a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + * which will iterate over the values of the associative array. The values are + * returned by reference. + * + * If structural changes are made to the array (removing or adding keys), all + * ranges previously obtained through this function are invalidated. The + * following example program will dereference a null pointer: + * + *--- + * import std.stdio : writeln; + * + * auto dict = ["k1": 1, "k2": 2]; + * auto valueRange = dict.byValue; + * dict.clear; + * writeln(valueRange.front); // Segmentation fault + *--- + * * Params: * aa = The associative array. * Returns: - * A forward range. + * A forward range referencing the values of the associative array. */ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { @@ -2925,7 +3117,7 @@ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe return Result(_aaToRange(aa)); } -/* ditto */ +/** ditto */ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc { return (*aa).byValue(); @@ -2943,11 +3135,35 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc } /*********************************** - * Returns a forward range over the key value pairs of the associative array. + * Returns a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) + * which will iterate over the key-value pairs of the associative array. The + * returned pairs are represented by an opaque type with `.key` and `.value` + * properties for accessing references to the key and value of the pair, + * respectively. + * + * If structural changes are made to the array (removing or adding keys), all + * ranges previously obtained through this function are invalidated. The + * following example program will dereference a null pointer: + * + *--- + * import std.stdio : writeln; + * + * auto dict = ["k1": 1, "k2": 2]; + * auto kvRange = dict.byKeyValue; + * dict.clear; + * writeln(kvRange.front.key, ": ", kvRange.front.value); // Segmentation fault + *--- + * + * Note that this is a low-level interface to iterating over the associative + * array and is not compatible withth the + * $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) type in Phobos. + * For compatibility with `Tuple`, use + * $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead. + * * Params: * aa = The associative array. * Returns: - * A forward range. + * A forward range referencing the pairs of the associative array. */ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe { @@ -2987,7 +3203,7 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe return Result(_aaToRange(aa)); } -/* ditto */ +/** ditto */ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc { return (*aa).byKeyValue(); @@ -2999,18 +3215,21 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc auto dict = ["k1": 1, "k2": 2]; int sum; foreach (e; dict.byKeyValue) + { + assert(e.key[1] == e.value + '0'); sum += e.value; + } assert(sum == 3); } /*********************************** - * Returns a dynamic array, the elements of which are the keys in the - * associative array. + * Returns a newly allocated dynamic array containing a copy of the keys from + * the associative array. * Params: * aa = The associative array. * Returns: - * A dynamic array. + * A dynamic array containing a copy of the keys. */ Key[] keys(T : Value[Key], Value, Key)(T aa) @property { @@ -3028,7 +3247,7 @@ Key[] keys(T : Value[Key], Value, Key)(T aa) @property return res; } -/* ditto */ +/** ditto */ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property { return (*aa).keys; @@ -3089,12 +3308,12 @@ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property } /*********************************** - * Returns a dynamic array, the elements of which are the values in the - * associative array. + * Returns a newly allocated dynamic array containing a copy of the values from + * the associative array. * Params: * aa = The associative array. * Returns: - * A dynamic array. + * A dynamic array containing a copy of the values. */ Value[] values(T : Value[Key], Value, Key)(T aa) @property { @@ -3112,7 +3331,7 @@ Value[] values(T : Value[Key], Value, Key)(T aa) @property return res; } -/* ditto */ +/** ditto */ Value[] values(T : Value[Key], Value, Key)(T *aa) @property { return (*aa).values; @@ -3188,12 +3407,13 @@ inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) return p ? *p : defaultValue; } -/* ditto */ +/** ditto */ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) { return (*aa).get(key, defaultValue); } +/// @safe unittest { auto aa = ["k1": 1]; @@ -4651,8 +4871,6 @@ public import core.internal.array.construction : _d_arrayctor; public import core.internal.array.construction : _d_arraysetctor; public import core.internal.array.capacity: _d_arraysetlengthTImpl; -public import core.lifetime : _d_delstructImpl; - public import core.internal.dassert: _d_assert_fail; public import core.internal.destruction: __ArrayDtor; @@ -4664,6 +4882,9 @@ public import core.internal.postblit: __ArrayPostblit; public import core.internal.switch_: __switch; public import core.internal.switch_: __switch_error; +public import core.lifetime : _d_delstructImpl; +public import core.lifetime : _d_newThrowable; + public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable); // Compare class and interface objects for ordering. diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index b363e3fea4f..47b67f130b8 100644 --- a/libphobos/libdruntime/rt/dmain2.d +++ b/libphobos/libdruntime/rt/dmain2.d @@ -624,7 +624,7 @@ extern (C) void _d_print_throwable(Throwable t) { WSink caption; if (t) - caption.sink(t.classinfo.name); + caption.sink(typeid(t).name); // Avoid static user32.dll dependency for console applications // by loading it dynamically as needed diff --git a/libphobos/libdruntime/rt/util/typeinfo.d b/libphobos/libdruntime/rt/util/typeinfo.d index d06254c092d..26c24c4c8f0 100644 --- a/libphobos/libdruntime/rt/util/typeinfo.d +++ b/libphobos/libdruntime/rt/util/typeinfo.d @@ -367,7 +367,10 @@ detect if we need to override. The overriding initializer should be nonzero. private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base) { static if (is(T == Base)) - override bool opEquals(Object o) { return TypeInfo.opEquals(o); } + override bool opEquals(const Object o) const @safe nothrow { return TypeInfo.opEquals(cast(const TypeInfo) o); } + + alias opEquals = typeof(super).opEquals; + alias opEquals = TypeInfo.opEquals; override string toString() const { return (T[]).stringof; } diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index b60fa170c4c..2babfbe6347 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -4687883231eba3bda7691321f2af107fdb3d0a44 +896b1d0e1e8b69bccac0e180ecd1b42a70f95d5b The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am index 94c6f4d4638..75f83974e99 100644 --- a/libphobos/src/Makefile.am +++ b/libphobos/src/Makefile.am @@ -89,11 +89,12 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \ std/algorithm/mutation.d std/algorithm/package.d \ std/algorithm/searching.d std/algorithm/setops.d \ std/algorithm/sorting.d std/array.d std/ascii.d std/base64.d \ - std/bigint.d std/bitmanip.d std/compiler.d std/complex.d \ - std/concurrency.d std/container/array.d std/container/binaryheap.d \ - std/container/dlist.d std/container/package.d std/container/rbtree.d \ - std/container/slist.d std/container/util.d std/conv.d std/csv.d \ - std/datetime/date.d std/datetime/interval.d std/datetime/package.d \ + std/bigint.d std/bitmanip.d std/checkedint.d std/compiler.d \ + std/complex.d std/concurrency.d std/container/array.d \ + std/container/binaryheap.d std/container/dlist.d \ + std/container/package.d std/container/rbtree.d std/container/slist.d \ + std/container/util.d std/conv.d std/csv.d std/datetime/date.d \ + std/datetime/interval.d std/datetime/package.d \ std/datetime/stopwatch.d std/datetime/systime.d \ std/datetime/timezone.d std/demangle.d std/digest/crc.d \ std/digest/digest.d std/digest/hmac.d std/digest/md.d \ diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in index a4101ebf028..f2395e2caf7 100644 --- a/libphobos/src/Makefile.in +++ b/libphobos/src/Makefile.in @@ -159,8 +159,9 @@ am__dirstamp = $(am__leading_dot)dirstamp @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/sorting.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/array.lo std/ascii.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/base64.lo std/bigint.lo \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/bitmanip.lo std/compiler.lo \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/complex.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/bitmanip.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/checkedint.lo \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/compiler.lo std/complex.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/concurrency.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/array.lo \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/binaryheap.lo \ @@ -549,11 +550,12 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/mutation.d std/algorithm/package.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/searching.d std/algorithm/setops.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/sorting.d std/array.d std/ascii.d std/base64.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/bigint.d std/bitmanip.d std/compiler.d std/complex.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/concurrency.d std/container/array.d std/container/binaryheap.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/dlist.d std/container/package.d std/container/rbtree.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/slist.d std/container/util.d std/conv.d std/csv.d \ -@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/date.d std/datetime/interval.d std/datetime/package.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/bigint.d std/bitmanip.d std/checkedint.d std/compiler.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/complex.d std/concurrency.d std/container/array.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/binaryheap.d std/container/dlist.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/package.d std/container/rbtree.d std/container/slist.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/util.d std/conv.d std/csv.d std/datetime/date.d \ +@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/interval.d std/datetime/package.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/stopwatch.d std/datetime/systime.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/datetime/timezone.d std/demangle.d std/digest/crc.d \ @ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/digest.d std/digest/hmac.d std/digest/md.d \ @@ -717,6 +719,7 @@ std/ascii.lo: std/$(am__dirstamp) std/base64.lo: std/$(am__dirstamp) std/bigint.lo: std/$(am__dirstamp) std/bitmanip.lo: std/$(am__dirstamp) +std/checkedint.lo: std/$(am__dirstamp) std/compiler.lo: std/$(am__dirstamp) std/complex.lo: std/$(am__dirstamp) std/concurrency.lo: std/$(am__dirstamp) diff --git a/libphobos/src/index.dd b/libphobos/src/index.dd index 8613a3c2b66..cbc173d891d 100644 --- a/libphobos/src/index.dd +++ b/libphobos/src/index.dd @@ -84,7 +84,7 @@ $(BOOKTABLE , ) $(LEADINGROW Data integrity) $(TR - $(TDNW $(MREF std,experimental,checkedint)) + $(TDNW $(MREF std,checkedint)) $(TD Checked integral types.) ) $(TR diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index ffdda8e1d4c..b86e0f99225 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -117,7 +117,7 @@ if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range) alias E = ForeachType!Range; static if (hasLength!Range) { - auto length = r.length; + const length = r.length; if (length == 0) return null; @@ -126,12 +126,35 @@ if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range) auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))(); // Every element of the uninitialized array must be initialized - size_t i; - foreach (e; r) + size_t cnt; //Number of elements that have been initialized + try { - emplaceRef!E(result[i], e); - ++i; + foreach (e; r) + { + emplaceRef!E(result[cnt], e); + ++cnt; + } + } catch (Exception e) + { + //https://issues.dlang.org/show_bug.cgi?id=22185 + //Make any uninitialized elements safely destructible. + foreach (ref elem; result[cnt..$]) + { + import core.internal.lifetime : emplaceInitializer; + emplaceInitializer(elem); + } + throw e; } + /* + https://issues.dlang.org/show_bug.cgi?id=22673 + + We preallocated an array, we should ensure that enough range elements + were gathered such that every slot in the array is filled. If not, the GC + will collect the allocated array, leading to the `length - cnt` left over elements + being collected too - despite their contents having no guarantee of destructibility. + */ + assert(length == cnt, + "Range .length property was not equal to the length yielded by the range before becoming empty"); return (() @trusted => cast(E[]) result)(); } else @@ -439,6 +462,91 @@ if (isAutodecodableString!String) assert(equal(r, [S(1), S(1)])); }); } +//https://issues.dlang.org/show_bug.cgi?id=22673 +@system unittest +{ + struct LyingRange + { + enum size_t length = 100; + enum theRealLength = 50; + size_t idx = 0; + bool empty() + { + return idx <= theRealLength; + } + void popFront() + { + ++idx; + } + size_t front() + { + return idx; + } + } + static assert(hasLength!LyingRange); + LyingRange rng; + import std.exception : assertThrown; + assertThrown!Error(array(rng)); +} +//https://issues.dlang.org/show_bug.cgi?id=22185 +@system unittest +{ + import std.stdio; + static struct ThrowingCopy + { + int x = 420; + this(ref return scope ThrowingCopy rhs) + { + rhs.x = 420; + // + throw new Exception("This throws"); + } + ~this() + { + /* + Any time this destructor runs, it should be running on "valid" + data. This is is mimicked by having a .init other than 0 (the value the memory + practically will be from the GC). + */ + if (x != 420) + { + //This will only trigger during GC finalization so avoid writefln for now. + printf("Destructor failure in ThrowingCopy(%d) @ %p", x, &this); + assert(x == 420, "unittest destructor failed"); + } + } + } + static struct LyingThrowingRange + { + enum size_t length = 100; + enum size_t evilRealLength = 50; + size_t idx; + ThrowingCopy front() + { + return ThrowingCopy(12); + } + bool empty() + { + return idx == evilRealLength; + } + void popFront() + { + ++idx; + } + } + static assert(hasLength!LyingThrowingRange); + import std.exception : assertThrown; + { + assertThrown(array(LyingThrowingRange())); + } + import core.memory : GC; + /* + Force a collection early. Doesn't always actually finalize the bad objects + but trying to collect soon after the allocation is thrown away means any potential failures + will happen earlier. + */ + GC.collect(); +} /** Returns a newly allocated associative array from a range of key/value tuples @@ -939,6 +1047,11 @@ if (isDynamicArray!T && allSatisfy!(isIntegral, I)) // from rt/lifetime.d private extern(C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; +// from rt/tracegc.d +version (D_ProfileGC) +private extern (C) void[] _d_newarrayUTrace(string file, size_t line, + string funcname, const scope TypeInfo ti, size_t length) pure nothrow; + private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow { static assert(I.length <= nDimensions!T, @@ -992,7 +1105,15 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow _d_newarrayU returns a void[], but with the length set according to E.sizeof. +/ - *(cast(void[]*)&ret) = _d_newarrayU(typeid(E[]), size); + version (D_ProfileGC) + { + // FIXME: file, line, function should be propagated from the + // caller, not here. + *(cast(void[]*)&ret) = _d_newarrayUTrace(__FILE__, __LINE__, + __FUNCTION__, typeid(E[]), size); + } + else + *(cast(void[]*)&ret) = _d_newarrayU(typeid(E[]), size); static if (minimallyInitialized && hasIndirections!E) // _d_newarrayU would have asserted if the multiplication below // had overflowed, so we don't have to check it again. diff --git a/libphobos/src/std/checkedint.d b/libphobos/src/std/checkedint.d new file mode 100644 index 00000000000..635c4207fcd --- /dev/null +++ b/libphobos/src/std/checkedint.d @@ -0,0 +1,3591 @@ +// Written in the D programming language. +/** +$(SCRIPT inhibitQuickIndex = 1;) + +This module defines facilities for efficient checking of integral operations +against overflow, casting with loss of precision, unexpected change of sign, +etc. The checking (and possibly correction) can be done at operation level, for +example $(LREF opChecked)$(D !"+"(x, y, overflow)) adds two integrals `x` and +`y` and sets `overflow` to `true` if an overflow occurred. The flag `overflow` +(a `bool` passed by reference) is not touched if the operation succeeded, so the +same flag can be reused for a sequence of operations and tested at the end. + +Issuing individual checked operations is flexible and efficient but often +tedious. The $(LREF Checked) facility offers encapsulated integral wrappers that +do all checking internally and have configurable behavior upon erroneous +results. For example, `Checked!int` is a type that behaves like `int` but aborts +execution immediately whenever involved in an operation that produces the +arithmetically wrong result. The accompanying convenience function $(LREF +checked) uses type deduction to convert a value `x` of integral type `T` to +`Checked!T` by means of `checked(x)`. For example: + +--- +void main() +{ + import std.checkedint, std.stdio; + writeln((checked(5) + 7).get); // 12 + writeln((checked(10) * 1000 * 1000 * 1000).get); // Overflow +} +--- + +Similarly, $(D checked(-1) > uint(0)) aborts execution (even though the built-in +comparison $(D int(-1) > uint(0)) is surprisingly true due to language's +conversion rules modeled after C). Thus, `Checked!int` is a virtually drop-in +replacement for `int` useable in debug builds, to be replaced by `int` in +release mode if efficiency demands it. + +`Checked` has customizable behavior with the help of a second type parameter, +`Hook`. Depending on what methods `Hook` defines, core operations on the +underlying integral may be verified for overflow or completely redefined. If +`Hook` defines no method at all and carries no state, there is no change in +behavior, i.e. $(D Checked!(int, void)) is a wrapper around `int` that adds no +customization at all. + +This module provides a few predefined hooks (below) that add useful behavior to +`Checked`: + +$(BOOKTABLE , + $(TR $(TD $(LREF Abort)) $(TD + fails every incorrect operation with a message to $(REF + stderr, std, stdio) followed by a call to `assert(0)`. It is the default + second parameter, i.e. `Checked!short` is the same as + $(D Checked!(short, Abort)). + )) + $(TR $(TD $(LREF Throw)) $(TD + fails every incorrect operation by throwing an exception. + )) + $(TR $(TD $(LREF Warn)) $(TD + prints incorrect operations to $(REF stderr, std, stdio) + but otherwise preserves the built-in behavior. + )) + $(TR $(TD $(LREF ProperCompare)) $(TD + fixes the comparison operators `==`, `!=`, `<`, `<=`, `>`, and `>=` + to return correct results in all circumstances, + at a slight cost in efficiency. For example, + $(D Checked!(uint, ProperCompare)(1) > -1) is `true`, + which is not the case for the built-in comparison. Also, comparing + numbers for equality with floating-point numbers only passes if the + integral can be converted to the floating-point number precisely, + so as to preserve transitivity of equality. + )) + $(TR $(TD $(LREF WithNaN)) $(TD + reserves a special "Not a Number" (NaN) value akin to the homonym value + reserved for floating-point values. Once a $(D Checked!(X, WithNaN)) + gets this special value, it preserves and propagates it until + reassigned. $(LREF isNaN) can be used to query whether the object + is not a number. + )) + $(TR $(TD $(LREF Saturate)) $(TD + implements saturating arithmetic, i.e. $(D Checked!(int, Saturate)) + "stops" at `int.max` for all operations that would cause an `int` to + overflow toward infinity, and at `int.min` for all operations that would + correspondingly overflow toward negative infinity. + )) +) + + +These policies may be used alone, e.g. $(D Checked!(uint, WithNaN)) defines a +`uint`-like type that reaches a stable NaN state for all erroneous operations. +They may also be "stacked" on top of each other, owing to the property that a +checked integral emulates an actual integral, which means another checked +integral can be built on top of it. Some combinations of interest include: + +$(BOOKTABLE , + $(TR $(TD $(D Checked!(Checked!int, ProperCompare)))) + $(TR $(TD +defines an `int` with fixed +comparison operators that will fail with `assert(0)` upon overflow. (Recall that +`Abort` is the default policy.) The order in which policies are combined is +important because the outermost policy (`ProperCompare` in this case) has the +first crack at intercepting an operator. The converse combination $(D +Checked!(Checked!(int, ProperCompare))) is meaningless because `Abort` will +intercept comparison and will fail without giving `ProperCompare` a chance to +intervene. + )) + $(TR $(TD)) + $(TR $(TDNW $(D Checked!(Checked!(int, ProperCompare), WithNaN)))) + $(TR $(TD +defines an `int`-like +type that supports a NaN value. For values that are not NaN, comparison works +properly. Again the composition order is important; $(D Checked!(Checked!(int, +WithNaN), ProperCompare)) does not have good semantics because `ProperCompare` +intercepts comparisons before the numbers involved are tested for NaN. + )) +) + +The hook's members are looked up statically in a Design by Introspection manner +and are all optional. The table below illustrates the members that a hook type +may define and their influence over the behavior of the `Checked` type using it. +In the table, `hook` is an alias for `Hook` if the type `Hook` does not +introduce any state, or an object of type `Hook` otherwise. + +$(TABLE , +$(TR $(TH `Hook` member) $(TH Semantics in $(D Checked!(T, Hook))) +) +$(TR $(TD `defaultValue`) $(TD If defined, `Hook.defaultValue!T` is used as the +default initializer of the payload.) +) +$(TR $(TD `min`) $(TD If defined, `Hook.min!T` is used as the minimum value of +the payload.) +) +$(TR $(TD `max`) $(TD If defined, `Hook.max!T` is used as the maximum value of +the payload.) +) +$(TR $(TD `hookOpCast`) $(TD If defined, `hook.hookOpCast!U(get)` is forwarded +to unconditionally when the payload is to be cast to type `U`.) +) +$(TR $(TD `onBadCast`) $(TD If defined and `hookOpCast` is $(I not) defined, +`onBadCast!U(get)` is forwarded to when the payload is to be cast to type `U` +and the cast would lose information or force a change of sign.) +) +$(TR $(TD `hookOpEquals`) $(TD If defined, $(D hook.hookOpEquals(get, rhs)) is +forwarded to unconditionally when the payload is compared for equality against +value `rhs` of integral, floating point, or Boolean type.) +) +$(TR $(TD `hookOpCmp`) $(TD If defined, $(D hook.hookOpCmp(get, rhs)) is +forwarded to unconditionally when the payload is compared for ordering against +value `rhs` of integral, floating point, or Boolean type.) +) +$(TR $(TD `hookOpUnary`) $(TD If defined, `hook.hookOpUnary!op(get)` (where `op` +is the operator symbol) is forwarded to for unary operators `-` and `~`. In +addition, for unary operators `++` and `--`, `hook.hookOpUnary!op(payload)` is +called, where `payload` is a reference to the value wrapped by `Checked` so the +hook can change it.) +) +$(TR $(TD `hookOpBinary`) $(TD If defined, $(D hook.hookOpBinary!op(get, rhs)) +(where `op` is the operator symbol and `rhs` is the right-hand side operand) is +forwarded to unconditionally for binary operators `+`, `-`, `*`, `/`, `%`, +`^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) +) +$(TR $(TD `hookOpBinaryRight`) $(TD If defined, $(D +hook.hookOpBinaryRight!op(lhs, get)) (where `op` is the operator symbol and +`lhs` is the left-hand side operand) is forwarded to unconditionally for binary +operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) +) +$(TR $(TD `onOverflow`) $(TD If defined, `hook.onOverflow!op(get)` is forwarded +to for unary operators that overflow but only if `hookOpUnary` is not defined. +Unary `~` does not overflow; unary `-` overflows only when the most negative +value of a signed type is negated, and the result of the hook call is returned. +When the increment or decrement operators overflow, the payload is assigned the +result of `hook.onOverflow!op(get)`. When a binary operator overflows, the +result of $(D hook.onOverflow!op(get, rhs)) is returned, but only if `Hook` does +not define `hookOpBinary`.) +) +$(TR $(TD `hookOpOpAssign`) $(TD If defined, $(D hook.hookOpOpAssign!op(payload, +rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side +operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, +`^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.) +) +$(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound)) +(where `value` is the value being assigned) is forwarded to when the result of +binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, +and `>>>=` is smaller than the smallest value representable by `T`.) +) +$(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound)) +(where `value` is the value being assigned) is forwarded to when the result of +binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, +and `>>>=` is larger than the largest value representable by `T`.) +) +$(TR $(TD `hookToHash`) $(TD If defined, $(D hook.hookToHash(payload)) +(where `payload` is a reference to the value wrapped by Checked) is forwarded +to when `toHash` is called on a Checked type. Custom hashing can be implemented +in a `Hook`, otherwise the built-in hashing is used.) +) +) + +Source: $(PHOBOSSRC std/checkedint.d) +*/ +module std.checkedint; +import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual; + +/// +@safe unittest +{ + int[] concatAndAdd(int[] a, int[] b, int offset) + { + // Aborts on overflow on size computation + auto r = new int[(checked(a.length) + b.length).get]; + // Aborts on overflow on element computation + foreach (i; 0 .. a.length) + r[i] = (a[i] + checked(offset)).get; + foreach (i; 0 .. b.length) + r[i + a.length] = (b[i] + checked(offset)).get; + return r; + } + assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]); +} + + +/// `Saturate` stops at an overflow +@safe unittest +{ + auto x = (cast(byte) 127).checked!Saturate; + assert(x == 127); + x++; + assert(x == 127); +} + +/// `WithNaN` has a special "Not a Number" (NaN) value akin to the homonym value reserved for floating-point values +@safe unittest +{ + auto x = 100.checked!WithNaN; + assert(x == 100); + x /= 0; + assert(x.isNaN); +} + +/// `ProperCompare` fixes the comparison operators ==, !=, <, <=, >, and >= to return correct results +@safe unittest +{ + uint x = 1; + auto y = x.checked!ProperCompare; + assert(x < -1); // built-in comparison + assert(y > -1); // ProperCompare +} + +/// `Throw` fails every incorrect operation by throwing an exception +@safe unittest +{ + import std.exception : assertThrown; + auto x = -1.checked!Throw; + assertThrown(x / 0); + assertThrown(x + int.min); + assertThrown(x == uint.max); +} + +/** +Checked integral type wraps an integral `T` and customizes its behavior with the +help of a `Hook` type. The type wrapped must be one of the predefined integrals +(unqualified), or another instance of `Checked`. + +Params: + T = type that is wrapped in the `Checked` type + Hook = hook type that customizes the behavior of the `Checked` type +*/ +struct Checked(T, Hook = Abort) +if (isIntegral!T || is(T == Checked!(U, H), U, H)) +{ + import std.algorithm.comparison : among; + import std.experimental.allocator.common : stateSize; + import std.format.spec : FormatSpec; + import std.range.primitives : isInputRange, ElementType; + import std.traits : hasMember, isSomeChar; + + /** + The type of the integral subject to checking. + */ + alias Representation = T; + + // state { + static if (hasMember!(Hook, "defaultValue")) + private T payload = Hook.defaultValue!T; + else + private T payload; + /** + `hook` is a member variable if it has state, or an alias for `Hook` + otherwise. + */ + static if (stateSize!Hook > 0) Hook hook; + else alias hook = Hook; + // } state + + // get + /** + Returns: + A copy of the underlying value. + */ + auto get() inout { return payload; } + /// + @safe unittest + { + auto x = checked(ubyte(42)); + static assert(is(typeof(x.get()) == ubyte)); + assert(x.get == 42); + const y = checked(ubyte(42)); + static assert(is(typeof(y.get()) == const ubyte)); + assert(y.get == 42); + } + + /** + Defines the minimum and maximum. These values are hookable by defining + `Hook.min` and/or `Hook.max`. + */ + static if (hasMember!(Hook, "min")) + { + enum Checked!(T, Hook) min = Checked!(T, Hook)(Hook.min!T); + /// + @safe unittest + { + assert(Checked!short.min == -32768); + assert(Checked!(short, WithNaN).min == -32767); + assert(Checked!(uint, WithNaN).max == uint.max - 1); + } + } + else + { + /// ditto + enum Checked!(T, Hook) min = Checked(T.min); + } + static if (hasMember!(Hook, "max")) + { + /// ditto + enum Checked!(T, Hook) max = Checked(Hook.max!T); + } + else + { + /// ditto + enum Checked!(T, Hook) max = Checked(T.max); + } + + /** + Constructor taking a value properly convertible to the underlying type. `U` + may be either an integral that can be converted to `T` without a loss, or + another `Checked` instance whose representation may be in turn converted to + `T` without a loss. + */ + this(U)(U rhs) + if (valueConvertible!(U, T) || + !isIntegral!T && is(typeof(T(rhs))) || + is(U == Checked!(V, W), V, W) && + is(typeof(Checked!(T, Hook)(rhs.get)))) + { + static if (isIntegral!U) + payload = rhs; + else + payload = rhs.payload; + } + /// + @safe unittest + { + auto a = checked(42L); + assert(a == 42); + auto b = Checked!long(4242); // convert 4242 to long + assert(b == 4242); + } + + /** + Assignment operator. Has the same constraints as the constructor. + + Params: + rhs = The value to assign + + Returns: + A reference to `this` + */ + ref Checked opAssign(U)(U rhs) return + if (is(typeof(Checked!(T, Hook)(rhs)))) + { + static if (isIntegral!U) + payload = rhs; + else + payload = rhs.payload; + return this; + } + /// + @safe unittest + { + Checked!long a; + a = 42L; + assert(a == 42); + a = 4242; + assert(a == 4242); + } + + /// + @safe unittest + { + Checked!long a, b; + a = b = 3; + assert(a == 3 && b == 3); + } + + /** + Construct from a decimal string. The conversion follows the same rules as + $(REF to, std, conv) converting a string to the wrapped `T` type. + + Params: + str = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + of characters + */ + this(Range)(Range str) + if (isInputRange!Range && isSomeChar!(ElementType!Range)) + { + import std.conv : to; + + this(to!T(str)); + } + + /** + $(REF to, std, conv) can convert a string to a `Checked!T`: + */ + @system unittest + { + import std.conv : to; + + const a = to!long("1234"); + const b = to!(Checked!long)("1234"); + assert(a == b); + } + + // opCast + /** + Casting operator to integral, `bool`, or floating point type. + + If a cast to a floating-point type is requested and `Hook` defines + `onBadCast`, the cast is verified by ensuring $(D get == cast(T) + U(get)). If that is not `true`, `hook.onBadCast!U(get)` is returned. + + If a cast to an integral type is requested and `Hook` defines `onBadCast`, + the cast is verified by ensuring `get` and $(D cast(U) + get) are the same arithmetic number. (Note that `int(-1)` and + `uint(1)` are different values arithmetically although they have the same + bitwise representation and compare equal by language rules.) If the numbers + are not arithmetically equal, `hook.onBadCast!U(get)` is + returned. + + Params: + U = The type to cast to + + Returns: + If `Hook` defines `hookOpCast`, the call immediately returns + `hook.hookOpCast!U(get)`. Otherwise, casting to `bool` yields $(D + get != 0) and casting to another integral that can represent all + values of `T` returns `get` promoted to `U`. + */ + U opCast(U, this _)() + if (isIntegral!U || isFloatingPoint!U || is(U == bool)) + { + static if (hasMember!(Hook, "hookOpCast")) + { + return hook.hookOpCast!U(payload); + } + else static if (is(U == bool)) + { + return payload != 0; + } + else static if (valueConvertible!(T, U)) + { + return payload; + } + // may lose bits or precision + else static if (!hasMember!(Hook, "onBadCast")) + { + return cast(U) payload; + } + else + { + if (isUnsigned!T || !isUnsigned!U || + T.sizeof > U.sizeof || payload >= 0) + { + auto result = cast(U) payload; + // If signedness is different, we need additional checks + if (result == payload && + (!isUnsigned!T || isUnsigned!U || result >= 0)) + return result; + } + return hook.onBadCast!U(payload); + } + } + /// + @safe unittest + { + assert(cast(uint) checked(42) == 42); + assert(cast(uint) checked!WithNaN(-42) == uint.max); + } + + // opEquals + /** + Compares `this` against `rhs` for equality. + + If `U` is also an instance of `Checked`, both hooks (left- and right-hand + side) are introspected for the method `hookOpEquals`. If both define it, + priority is given to the left-hand side. + + Params: + rhs = Right-hand side to compare for equality + + Returns: + If `Hook` defines `hookOpEquals`, the function forwards to $(D + hook.hookOpEquals(get, rhs)). Otherwise, the result of the + built-in operation $(D get == rhs) is returned. + + */ + bool opEquals(U, this _)(U rhs) + if (isIntegral!U || isFloatingPoint!U || is(U == bool) || + is(U == Checked!(V, W), V, W) && is(typeof(this == rhs.payload))) + { + static if (is(U == Checked!(V, W), V, W)) + { + alias R = typeof(payload + rhs.payload); + static if (is(Hook == W)) + { + // Use the lhs hook if there + return this == rhs.payload; + } + else static if (valueConvertible!(T, R) && valueConvertible!(V, R)) + { + return payload == rhs.payload; + } + else static if (hasMember!(Hook, "hookOpEquals")) + { + return hook.hookOpEquals(payload, rhs.payload); + } + else static if (hasMember!(W, "hookOpEquals")) + { + return rhs.hook.hookOpEquals(rhs.payload, payload); + } + else + { + return payload == rhs.payload; + } + } + else static if (hasMember!(Hook, "hookOpEquals")) + return hook.hookOpEquals(payload, rhs); + else static if (isIntegral!U || isFloatingPoint!U || is(U == bool)) + return payload == rhs; + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + import std.traits : isUnsigned; + + static struct MyHook + { + static bool thereWereErrors; + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + if (lhs != rhs) return false; + static if (isUnsigned!L && !isUnsigned!R) + { + if (lhs > 0 && rhs < 0) thereWereErrors = true; + } + else static if (isUnsigned!R && !isUnsigned!L) + if (lhs < 0 && rhs > 0) thereWereErrors = true; + // Preserve built-in behavior. + return true; + } + } + auto a = checked!MyHook(-42); + assert(a == uint(-42)); + assert(MyHook.thereWereErrors); + MyHook.thereWereErrors = false; + assert(checked!MyHook(uint(-42)) == -42); + assert(MyHook.thereWereErrors); + static struct MyHook2 + { + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + return lhs == rhs; + } + } + MyHook.thereWereErrors = false; + assert(checked!MyHook2(uint(-42)) == a); + // Hook on left hand side takes precedence, so no errors + assert(!MyHook.thereWereErrors); + } + + // toHash + /** + Generates a hash for `this`. If `Hook` defines `hookToHash`, the call + immediately returns `hook.hookToHash(payload)`. If `Hook` does not + implement `hookToHash`, but it has state, a hash will be generated for + the `Hook` using the built-in function and it will be xored with the + hash of the `payload`. + + Returns: + The hash of `this` instance. + + */ + size_t toHash() const nothrow @safe + { + static if (hasMember!(Hook, "hookToHash")) + { + return hook.hookToHash(payload); + } + else static if (stateSize!Hook > 0) + { + static if (hasMember!(typeof(payload), "toHash")) + { + return payload.toHash() ^ hashOf(hook); + } + else + { + return hashOf(payload) ^ hashOf(hook); + } + } + else static if (hasMember!(typeof(payload), "toHash")) + { + return payload.toHash(); + } + else + { + return .hashOf(payload); + } + } + + /// ditto + size_t toHash(this _)() shared const nothrow @safe + { + import core.atomic : atomicLoad, MemoryOrder; + static if (is(typeof(this.payload.atomicLoad!(MemoryOrder.acq)) P)) + { + auto payload = __ctfe ? cast(P) this.payload + : this.payload.atomicLoad!(MemoryOrder.acq); + } + else + { + alias payload = this.payload; + } + + static if (hasMember!(Hook, "hookToHash")) + { + return hook.hookToHash(payload); + } + else static if (stateSize!Hook > 0) + { + static if (hasMember!(typeof(payload), "toHash")) + { + return payload.toHash() ^ hashOf(hook); + } + else + { + return hashOf(payload) ^ hashOf(hook); + } + } + else static if (hasMember!(typeof(payload), "toHash")) + { + return payload.toHash(); + } + else + { + return .hashOf(payload); + } + } + + /** + Writes a string representation of this to a `sink`. + + Params: + sink = A `Char` accepting + $(REF_ALTTEXT output range, isOutputRange, std,range,primitives). + fmt = A $(REF FormatSpec, std, format) which controls how this + is formatted. + */ + void toString(Writer, Char)(scope ref Writer sink, scope const ref FormatSpec!Char fmt) const + { + import std.format.write : formatValue; + if (fmt.spec == 's') + return formatValue(sink, this, fmt); + else + return formatValue(sink, payload, fmt); + } + + /** + `toString` is rarely directly invoked; the usual way of using it is via + $(REF format, std, format): + */ + @system unittest + { + import std.format; + + assert(format("%04d", checked(15)) == "0015"); + assert(format("0x%02x", checked(15)) == "0x0f"); + } + + // opCmp + /** + + Compares `this` against `rhs` for ordering. If `Hook` defines `hookOpCmp`, + the function forwards to $(D hook.hookOpCmp(get, rhs)). Otherwise, the + result of the built-in comparison operation is returned. + + If `U` is also an instance of `Checked`, both hooks (left- and right-hand + side) are introspected for the method `hookOpCmp`. If both define it, + priority is given to the left-hand side. + + Params: + rhs = The right-hand side operand + U = either the type of `rhs` or the underlying type + if `rhs` is a `Checked` instance + Hook1 = If `rhs` is a `Checked` instance, `Hook1` represents + the instance's behavior hook + + Returns: + The result of `hookOpCmp` if `hook` defines `hookOpCmp`. If + `U` is an instance of `Checked` and `hook` does not define + `hookOpCmp`, result of `rhs.hook.hookOpCmp` is returned. + If none of the instances specify the behavior via `hookOpCmp`, + `-1` is returned if `lhs` is lesser than `rhs`, `1` if `lhs` + is greater than `rhs` and `0` on equality. + */ + auto opCmp(U, this _)(const U rhs) //const pure @safe nothrow @nogc + if (isIntegral!U || isFloatingPoint!U || is(U == bool)) + { + static if (hasMember!(Hook, "hookOpCmp")) + { + return hook.hookOpCmp(payload, rhs); + } + else static if (valueConvertible!(T, U) || valueConvertible!(U, T)) + { + return payload < rhs ? -1 : payload > rhs; + } + else static if (isFloatingPoint!U) + { + U lhs = payload; + return lhs < rhs ? U(-1.0) + : lhs > rhs ? U(1.0) + : lhs == rhs ? U(0.0) : U.init; + } + else + { + return payload < rhs ? -1 : payload > rhs; + } + } + + /// ditto + auto opCmp(U, Hook1, this _)(Checked!(U, Hook1) rhs) + { + alias R = typeof(payload + rhs.payload); + static if (valueConvertible!(T, R) && valueConvertible!(U, R)) + { + return payload < rhs.payload ? -1 : payload > rhs.payload; + } + else static if (is(Hook == Hook1)) + { + // Use the lhs hook + return this.opCmp(rhs.payload); + } + else static if (hasMember!(Hook, "hookOpCmp")) + { + return hook.hookOpCmp(get, rhs.get); + } + else static if (hasMember!(Hook1, "hookOpCmp")) + { + return -rhs.hook.hookOpCmp(rhs.payload, get); + } + else + { + return payload < rhs.payload ? -1 : payload > rhs.payload; + } + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + import std.traits : isUnsigned; + + static struct MyHook + { + static bool thereWereErrors; + static int hookOpCmp(L, R)(L lhs, R rhs) + { + static if (isUnsigned!L && !isUnsigned!R) + { + if (rhs < 0 && rhs >= lhs) + thereWereErrors = true; + } + else static if (isUnsigned!R && !isUnsigned!L) + { + if (lhs < 0 && lhs >= rhs) + thereWereErrors = true; + } + // Preserve built-in behavior. + return lhs < rhs ? -1 : lhs > rhs; + } + } + auto a = checked!MyHook(-42); + assert(a > uint(42)); + assert(MyHook.thereWereErrors); + static struct MyHook2 + { + static int hookOpCmp(L, R)(L lhs, R rhs) + { + // Default behavior + return lhs < rhs ? -1 : lhs > rhs; + } + } + MyHook.thereWereErrors = false; + assert(Checked!(uint, MyHook2)(uint(-42)) <= a); + //assert(Checked!(uint, MyHook2)(uint(-42)) >= a); + // Hook on left hand side takes precedence, so no errors + assert(!MyHook.thereWereErrors); + assert(a <= Checked!(uint, MyHook2)(uint(-42))); + assert(MyHook.thereWereErrors); + } + + // For coverage + static if (is(T == int) && is(Hook == void)) @safe unittest + { + assert(checked(42) <= checked!void(42)); + assert(checked!void(42) <= checked(42u)); + assert(checked!void(42) <= checked!(void*)(42u)); + } + + // opUnary + /** + + Defines unary operators `+`, `-`, `~`, `++`, and `--`. Unary `+` is not + overridable and always has built-in behavior (returns `this`). For the + others, if `Hook` defines `hookOpUnary`, `opUnary` forwards to $(D + Checked!(typeof(hook.hookOpUnary!op(get)), + Hook)(hook.hookOpUnary!op(get))). + + If `Hook` does not define `hookOpUnary` but defines `onOverflow`, `opUnary` + forwards to `hook.onOverflow!op(get)` in case an overflow occurs. + For `++` and `--`, the payload is assigned from the result of the call to + `onOverflow`. + + Note that unary `-` is considered to overflow if `T` is a signed integral of + 32 or 64 bits and is equal to the most negative value. This is because that + value has no positive negation. + + Params: + op = The unary operator + + Returns: + A `Checked` instance representing the result of the unary + operation + */ + auto opUnary(string op, this _)() + if (op == "+" || op == "-" || op == "~") + { + static if (op == "+") + return Checked(this); // "+" is not hookable + else static if (hasMember!(Hook, "hookOpUnary")) + { + auto r = hook.hookOpUnary!op(payload); + return Checked!(typeof(r), Hook)(r); + } + else static if (op == "-" && isIntegral!T && T.sizeof >= 4 && + !isUnsigned!T && hasMember!(Hook, "onOverflow")) + { + static assert(is(typeof(-payload) == typeof(payload))); + bool overflow; + import core.checkedint : negs; + auto r = negs(payload, overflow); + if (overflow) r = hook.onOverflow!op(payload); + return Checked(r); + } + else + return Checked(mixin(op ~ "payload")); + } + + /// ditto + ref Checked opUnary(string op)() return + if (op == "++" || op == "--") + { + static if (hasMember!(Hook, "hookOpUnary")) + hook.hookOpUnary!op(payload); + else static if (hasMember!(Hook, "onOverflow")) + { + static if (op == "++") + { + if (payload == max.payload) + payload = hook.onOverflow!"++"(payload); + else + ++payload; + } + else + { + if (payload == min.payload) + payload = hook.onOverflow!"--"(payload); + else + --payload; + } + } + else + mixin(op ~ "payload;"); + return this; + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + static struct MyHook + { + static bool thereWereErrors; + static L hookOpUnary(string x, L)(L lhs) + { + if (x == "-" && lhs == -lhs) thereWereErrors = true; + return -lhs; + } + } + auto a = checked!MyHook(long.min); + assert(a == -a); + assert(MyHook.thereWereErrors); + auto b = checked!void(42); + assert(++b == 43); + } + + // opBinary + /** + + Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, + and `>>>`. If `Hook` defines `hookOpBinary`, `opBinary` forwards to $(D + Checked!(typeof(hook.hookOpBinary!op(get, rhs)), + Hook)(hook.hookOpBinary!op(get, rhs))). + + If `Hook` does not define `hookOpBinary` but defines `onOverflow`, + `opBinary` forwards to `hook.onOverflow!op(get, rhs)` in case an + overflow occurs. + + If two `Checked` instances are involved in a binary operation and both + define `hookOpBinary`, the left-hand side hook has priority. If both define + `onOverflow`, a compile-time error occurs. + + Params: + op = The binary operator + rhs = The right hand side operand + U = If `rhs` is a `Checked` instance, `U` represents + the underlying instance type + Hook1 = If `rhs` is a `Checked` instance, `Hook1` represents + the instance's behavior hook + + Returns: + A `Checked` instance representing the result of the binary + operation + */ + auto opBinary(string op, Rhs)(const Rhs rhs) + if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) + { + return opBinaryImpl!(op, Rhs, typeof(this))(rhs); + } + + /// ditto + auto opBinary(string op, Rhs)(const Rhs rhs) const + if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) + { + return opBinaryImpl!(op, Rhs, typeof(this))(rhs); + } + + private auto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs) + { + alias R = typeof(mixin("payload" ~ op ~ "rhs")); + static assert(is(typeof(mixin("payload" ~ op ~ "rhs")) == R)); + static if (isIntegral!R) alias Result = Checked!(R, Hook); + else alias Result = R; + + static if (hasMember!(Hook, "hookOpBinary")) + { + auto r = hook.hookOpBinary!op(payload, rhs); + return Checked!(typeof(r), Hook)(r); + } + else static if (is(Rhs == bool)) + { + return mixin("this" ~ op ~ "ubyte(rhs)"); + } + else static if (isFloatingPoint!Rhs) + { + return mixin("payload" ~ op ~ "rhs"); + } + else static if (hasMember!(Hook, "onOverflow")) + { + bool overflow; + auto r = opChecked!op(payload, rhs, overflow); + if (overflow) r = hook.onOverflow!op(payload, rhs); + return Result(r); + } + else + { + // Default is built-in behavior + return Result(mixin("payload" ~ op ~ "rhs")); + } + } + + /// ditto + auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) + { + return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); + } + + /// ditto + auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) const + { + return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); + } + + private + auto opBinaryImpl2(string op, U, Hook1, this _)(Checked!(U, Hook1) rhs) + { + alias R = typeof(get + rhs.payload); + static if (valueConvertible!(T, R) && valueConvertible!(U, R) || + is(Hook == Hook1)) + { + // Delegate to lhs + return mixin("this" ~ op ~ "rhs.payload"); + } + else static if (hasMember!(Hook, "hookOpBinary")) + { + return hook.hookOpBinary!op(payload, rhs); + } + else static if (hasMember!(Hook1, "hookOpBinary")) + { + // Delegate to rhs + return mixin("this.payload" ~ op ~ "rhs"); + } + else static if (hasMember!(Hook, "onOverflow") && + !hasMember!(Hook1, "onOverflow")) + { + // Delegate to lhs + return mixin("this" ~ op ~ "rhs.payload"); + } + else static if (hasMember!(Hook1, "onOverflow") && + !hasMember!(Hook, "onOverflow")) + { + // Delegate to rhs + return mixin("this.payload" ~ op ~ "rhs"); + } + else + { + static assert(0, "Conflict between lhs and rhs hooks," ~ + " use .get on one side to disambiguate."); + } + } + + static if (is(T == int) && is(Hook == void)) @safe unittest + { + const a = checked(42); + assert(a + 1 == 43); + assert(a + checked(uint(42)) == 84); + assert(checked(42) + checked!void(42u) == 84); + assert(checked!void(42) + checked(42u) == 84); + + static struct MyHook + { + static uint tally; + static auto hookOpBinary(string x, L, R)(L lhs, R rhs) + { + ++tally; + return mixin("lhs" ~ x ~ "rhs"); + } + } + assert(checked!MyHook(42) + checked(42u) == 84); + assert(checked!void(42) + checked!MyHook(42u) == 84); + assert(MyHook.tally == 2); + } + + // opBinaryRight + /** + + Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, + `>>`, and `>>>` for the case when a built-in numeric or Boolean type is on + the left-hand side, and a `Checked` instance is on the right-hand side. + + Params: + op = The binary operator + lhs = The left hand side operand + + Returns: + A `Checked` instance representing the result of the binary + operation + + */ + auto opBinaryRight(string op, Lhs)(const Lhs lhs) + if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) + { + return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); + } + + /// ditto + auto opBinaryRight(string op, Lhs)(const Lhs lhs) const + if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) + { + return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); + } + + private auto opBinaryRightImpl(string op, Lhs, this _)(const Lhs lhs) + { + static if (hasMember!(Hook, "hookOpBinaryRight")) + { + auto r = hook.hookOpBinaryRight!op(lhs, payload); + return Checked!(typeof(r), Hook)(r); + } + else static if (hasMember!(Hook, "hookOpBinary")) + { + auto r = hook.hookOpBinary!op(lhs, payload); + return Checked!(typeof(r), Hook)(r); + } + else static if (is(Lhs == bool)) + { + return mixin("ubyte(lhs)" ~ op ~ "this"); + } + else static if (isFloatingPoint!Lhs) + { + return mixin("lhs" ~ op ~ "payload"); + } + else static if (hasMember!(Hook, "onOverflow")) + { + bool overflow; + auto r = opChecked!op(lhs, T(payload), overflow); + if (overflow) r = hook.onOverflow!op(lhs, payload); + return Checked!(typeof(r), Hook)(r); + } + else + { + // Default is built-in behavior + auto r = mixin("lhs" ~ op ~ "T(payload)"); + return Checked!(typeof(r), Hook)(r); + } + } + + static if (is(T == int) && is(Hook == void)) @safe unittest + { + assert(1 + checked(1) == 2); + static uint tally; + static struct MyHook + { + static auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) + { + ++tally; + return mixin("lhs" ~ x ~ "rhs"); + } + } + assert(1 + checked!MyHook(1) == 2); + assert(tally == 1); + + immutable x1 = checked(1); + assert(1 + x1 == 2); + immutable x2 = checked!MyHook(1); + assert(1 + x2 == 2); + assert(tally == 2); + } + + // opOpAssign + /** + + Defines operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, + `<<=`, `>>=`, and `>>>=`. + + If `Hook` defines `hookOpOpAssign`, `opOpAssign` forwards to + `hook.hookOpOpAssign!op(payload, rhs)`, where `payload` is a reference to + the internally held data so the hook can change it. + + Otherwise, the operator first evaluates $(D auto result = + opBinary!op(payload, rhs).payload), which is subject to the hooks in + `opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if + `Hook` defines `onLowerBound`, the payload is assigned from $(D + hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T, + Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned + from $(D hook.onUpperBound(result, min)). + + If the right-hand side is also a Checked but with a different hook or + underlying type, the hook and underlying type of this Checked takes + precedence. + + In all other cases, the built-in behavior is carried out. + + Params: + op = The operator involved (without the `"="`, e.g. `"+"` for `"+="` etc) + rhs = The right-hand side of the operator (left-hand side is `this`) + + Returns: A reference to `this`. + */ + ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return + if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) + { + static assert(is(typeof(mixin("payload" ~ op ~ "=rhs")) == T)); + + static if (hasMember!(Hook, "hookOpOpAssign")) + { + hook.hookOpOpAssign!op(payload, rhs); + } + else + { + alias R = typeof(get + rhs); + auto r = opBinary!op(rhs).get; + import std.conv : unsigned; + + static if (ProperCompare.hookOpCmp(R.min, min.get) < 0 && + hasMember!(Hook, "onLowerBound")) + { + if (ProperCompare.hookOpCmp(r, min.get) < 0) + { + // Example: Checked!uint(1) += int(-3) + payload = hook.onLowerBound(r, min.get); + return this; + } + } + static if (ProperCompare.hookOpCmp(max.get, R.max) < 0 && + hasMember!(Hook, "onUpperBound")) + { + if (ProperCompare.hookOpCmp(r, max.get) > 0) + { + // Example: Checked!uint(1) += long(uint.max) + payload = hook.onUpperBound(r, max.get); + return this; + } + } + payload = cast(T) r; + } + return this; + } + + /// ditto + ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return + if (is(Rhs == Checked!(RhsT, RhsHook), RhsT, RhsHook)) + { + return opOpAssign!(op, typeof(rhs.payload))(rhs.payload); + } + + /// + static if (is(T == int) && is(Hook == void)) @safe unittest + { + static struct MyHook + { + static bool thereWereErrors; + static T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + thereWereErrors = true; + return bound; + } + static T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + thereWereErrors = true; + return bound; + } + } + auto x = checked!MyHook(byte.min); + x -= 1; + assert(MyHook.thereWereErrors); + MyHook.thereWereErrors = false; + x = byte.max; + x += 1; + assert(MyHook.thereWereErrors); + } +} + +/// +@safe @nogc pure nothrow unittest +{ + // Hook that ignores all problems. + static struct Ignore + { + @nogc nothrow pure @safe static: + Dst onBadCast(Dst, Src)(Src src) { return cast(Dst) src; } + Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; } + T onUpperBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; } + bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return lhs == rhs; } + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return (lhs > rhs) - (lhs < rhs); } + typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) { return mixin(x ~ "lhs"); } + typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + static if (x == "/") + return typeof(lhs / rhs).min; + else + return mixin("lhs" ~ x ~ "rhs"); + } + } + + auto x = Checked!(int, Ignore)(5) + 7; +} + + +/** + +Convenience function that turns an integral into the corresponding `Checked` +instance by using template argument deduction. The hook type may be specified +(by default `Abort`). + +Params: + Hook = type that customizes the behavior, by default `Abort` + T = type represetinfg the underlying represantion of the `Checked` instance + value = the actual value of the representation + +Returns: + A `Checked` instance customized by the provided `Hook` and `value` +*/ +Checked!(T, Hook) checked(Hook = Abort, T)(const T value) +if (is(typeof(Checked!(T, Hook)(value)))) +{ + return Checked!(T, Hook)(value); +} + +/// +@safe unittest +{ + static assert(is(typeof(checked(42)) == Checked!int)); + assert(checked(42) == Checked!int(42)); + static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN))); + assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42)); +} + +// get +@safe unittest +{ + void test(T)() + { + assert(Checked!(T, void)(ubyte(22)).get == 22); + } + test!ubyte; + test!(const ubyte); + test!(immutable ubyte); +} + +@system unittest +{ + // https://issues.dlang.org/show_bug.cgi?id=21758 + assert(4 * checked(5L) == 20); + assert(20 / checked(5L) == 4); + assert(2 ^^ checked(3L) == 8); + assert(12 % checked(5L) == 2); + assert((0xff & checked(3L)) == 3); + assert((0xf0 | checked(3L)) == 0xf3); + assert((0xff ^ checked(3L)) == 0xfc); +} + +// Abort +/** + +Force all integral errors to fail by printing an error message to `stderr` and +then abort the program. `Abort` is the default second argument for `Checked`. + +*/ +struct Abort +{ +static: + /** + + Called automatically upon a bad cast (one that loses precision or attempts + to convert a negative value to an unsigned type). The source type is `Src` + and the destination type is `Dst`. + + Params: + src = Souce operand + + Returns: + Nominally the result is the desired value of the cast operation, + which will be forwarded as the result of the cast. For `Abort`, the + function never returns because it aborts the program. + */ + Dst onBadCast(Dst, Src)(Src src) + { + Warn.onBadCast!Dst(src); + assert(0); + } + + /** + + Called automatically upon a bounds error. + + Params: + rhs = The right-hand side value in the assignment, after the operator has + been evaluated + bound = The value of the bound being violated + + Returns: Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Abort`, the function never returns because + it aborts the program. + + */ + T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + Warn.onLowerBound(rhs, bound); + assert(0); + } + /// ditto + T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + Warn.onUpperBound(rhs, bound); + assert(0); + } + + /** + + Called automatically upon a comparison for equality. In case of a erroneous + comparison (one that would make a signed negative value appear equal to an + unsigned positive value), this hook issues `assert(0)` which terminates the + application. + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: Upon a correct comparison, returns the result of the comparison. + Otherwise, the function terminates the application so it never returns. + + */ + static bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + Warn.hookOpEquals(lhs, rhs); + assert(0); + } + return result; + } + + /** + + Called automatically upon a comparison for ordering using one of the + operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. + it would make a signed negative value appear greater than or equal to an + unsigned positive value), then application is terminated with `assert(0)`. + Otherwise, the three-state result is returned (positive if $(D lhs > rhs), + negative if $(D lhs < rhs), `0` otherwise). + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: For correct comparisons, returns a positive integer if $(D lhs > + rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. Upon + a mistaken comparison such as $(D int(-1) < uint(0)), the function never + returns because it aborts the program. + + */ + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"cmp"(lhs, rhs, error); + if (error) + { + Warn.hookOpCmp(lhs, rhs); + assert(0); + } + return result; + } + + /** + + Called automatically upon an overflow during a unary or binary operation. + + Params: + x = The operator, e.g. `-` + lhs = The left-hand side (or sole) argument + rhs = The right-hand side type involved in the operator + + Returns: Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Abort`, the function never returns because + it aborts the program. + + */ + typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) + { + Warn.onOverflow!x(lhs); + assert(0); + } + /// ditto + typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + Warn.onOverflow!x(lhs, rhs); + assert(0); + } +} + +/// +@safe unittest +{ + void test(T)() + { + Checked!(int, Abort) x; + x = 42; + auto x1 = cast(T) x; + assert(x1 == 42); + //x1 += long(int.max); + } + test!short; + test!(const short); + test!(immutable short); +} + + +// Throw +/** + +Force all integral errors to fail by throwing an exception of type +`Throw.CheckFailure`. The message coming with the error is similar to the one +printed by `Warn`. + +*/ +struct Throw +{ + /** + Exception type thrown upon any failure. + */ + static class CheckFailure : Exception + { + /** + Params: + f = format specifier + vals = actual values for the format specifier + */ + this(T...)(string f, T vals) + { + import std.format : format; + super(format(f, vals)); + } + } + + /** + + Called automatically upon a bad cast (one that loses precision or attempts + to convert a negative value to an unsigned type). The source type is `Src` + and the destination type is `Dst`. + + Params: + src = source operand + + Returns: + Nominally the result is the desired value of the cast operation, + which will be forwarded as the result of the cast. For `Throw`, the + function never returns because it throws an exception. + + Throws: + `CheckFailure` on bad cast + */ + static Dst onBadCast(Dst, Src)(Src src) + { + throw new CheckFailure("Erroneous cast: cast(%s) %s(%s)", + Dst.stringof, Src.stringof, src); + } + + /** + + Called automatically upon a bounds error. + + Params: + rhs = The right-hand side value in the assignment, after the operator has + been evaluated + bound = The value of the bound being violated + + Returns: + Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Throw`, the function never returns because + it throws. + + Throws: + `CheckFailure` on overflow + + */ + static T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + throw new CheckFailure("Lower bound error: %s(%s) < %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + } + /// ditto + static T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + throw new CheckFailure("Upper bound error: %s(%s) > %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + } + + /** + + Called automatically upon a comparison for equality. Throws upon an + erroneous comparison (one that would make a signed negative value appear + equal to an unsigned positive value). + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: The result of the comparison. + + Throws: `CheckFailure` if the comparison is mathematically erroneous. + + */ + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + throw new CheckFailure("Erroneous comparison: %s(%s) == %s(%s)", + L.stringof, lhs, R.stringof, rhs); + } + return result; + } + + /** + + Called automatically upon a comparison for ordering using one of the + operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. + it would make a signed negative value appear greater than or equal to an + unsigned positive value), throws a `Throw.CheckFailure` exception. + Otherwise, the three-state result is returned (positive if $(D lhs > rhs), + negative if $(D lhs < rhs), `0` otherwise). + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: For correct comparisons, returns a positive integer if $(D lhs > + rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. + + Throws: Upon a mistaken comparison such as $(D int(-1) < uint(0)), the + function never returns because it throws a `Throw.CheckedFailure` exception. + + */ + static int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"cmp"(lhs, rhs, error); + if (error) + { + throw new CheckFailure("Erroneous ordering comparison: %s(%s) and %s(%s)", + Lhs.stringof, lhs, Rhs.stringof, rhs); + } + return result; + } + + /** + + Called automatically upon an overflow during a unary or binary operation. + + Params: + x = The operator, e.g. `-` + lhs = The left-hand side (or sole) argument + rhs = The right-hand side type involved in the operator + + Returns: + Nominally the result is the desired value of the operator, which + will be forwarded as result. For `Throw`, the function never returns because + it throws an exception. + + Throws: + `CheckFailure` on overflow + + */ + static typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) + { + throw new CheckFailure("Overflow on unary operator: %s%s(%s)", + x, Lhs.stringof, lhs); + } + /// ditto + static typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + throw new CheckFailure("Overflow on binary operator: %s(%s) %s %s(%s)", + Lhs.stringof, lhs, x, Rhs.stringof, rhs); + } +} + +/// +@safe unittest +{ + void test(T)() + { + Checked!(int, Throw) x; + x = 42; + auto x1 = cast(T) x; + assert(x1 == 42); + x = T.max + 1; + import std.exception : assertThrown, assertNotThrown; + assertThrown(cast(T) x); + x = x.max; + assertThrown(x += 42); + assertThrown(x += 42L); + x = x.min; + assertThrown(-x); + assertThrown(x -= 42); + assertThrown(x -= 42L); + x = -1; + assertNotThrown(x == -1); + assertThrown(x == uint(-1)); + assertNotThrown(x <= -1); + assertThrown(x <= uint(-1)); + } + test!short; + test!(const short); + test!(immutable short); +} + +// Warn +/** +Hook that prints to `stderr` a trace of all integral errors, without affecting +default behavior. +*/ +struct Warn +{ + import std.stdio : writefln; +static: + /** + + Called automatically upon a bad cast from `src` to type `Dst` (one that + loses precision or attempts to convert a negative value to an unsigned + type). + + Params: + src = The source of the cast + Dst = The target type of the cast + + Returns: `cast(Dst) src` + + */ + Dst onBadCast(Dst, Src)(Src src) + { + trustedStderr.writefln("Erroneous cast: cast(%s) %s(%s)", + Dst.stringof, Src.stringof, src); + return cast(Dst) src; + } + + /** + + Called automatically upon a bad `opOpAssign` call (one that loses precision + or attempts to convert a negative value to an unsigned type). + + Params: + rhs = The right-hand side value in the assignment, after the operator has + been evaluated + bound = The bound being violated + + Returns: `cast(T) rhs` + */ + T onLowerBound(Rhs, T)(Rhs rhs, T bound) + { + trustedStderr.writefln("Lower bound error: %s(%s) < %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + return cast(T) rhs; + } + /// ditto + T onUpperBound(Rhs, T)(Rhs rhs, T bound) + { + trustedStderr.writefln("Upper bound error: %s(%s) > %s(%s)", + Rhs.stringof, rhs, T.stringof, bound); + return cast(T) rhs; + } + + /** + + Called automatically upon a comparison for equality. In case of an Erroneous + comparison (one that would make a signed negative value appear equal to an + unsigned positive value), writes a warning message to `stderr` as a side + effect. + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: In all cases the function returns the built-in result of $(D lhs == + rhs). + + */ + bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + trustedStderr.writefln("Erroneous comparison: %s(%s) == %s(%s)", + Lhs.stringof, lhs, Rhs.stringof, rhs); + return lhs == rhs; + } + return result; + } + + /// + @safe unittest + { + auto x = checked!Warn(-42); + // Passes + assert(x == -42); + // Passes but prints a warning + // assert(x == uint(-42)); + } + + /** + + Called automatically upon a comparison for ordering using one of the + operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. + it would make a signed negative value appear greater than or equal to an + unsigned positive value), then a warning message is printed to `stderr`. + + Params: + lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + rhs = The right-hand side type involved in the operator + + Returns: In all cases, returns $(D lhs < rhs ? -1 : lhs > rhs). The result + is not autocorrected in case of an erroneous comparison. + + */ + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + bool error; + auto result = opChecked!"cmp"(lhs, rhs, error); + if (error) + { + trustedStderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)", + Lhs.stringof, lhs, Rhs.stringof, rhs); + return lhs < rhs ? -1 : lhs > rhs; + } + return result; + } + + /// + @safe unittest + { + auto x = checked!Warn(-42); + // Passes + assert(x <= -42); + // Passes but prints a warning + // assert(x <= uint(-42)); + } + + /** + + Called automatically upon an overflow during a unary or binary operation. + + Params: + x = The operator involved + Lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of + the operator is `Checked!int` + Rhs = The right-hand side type involved in the operator + + Returns: + $(D mixin(x ~ "lhs")) for unary, $(D mixin("lhs" ~ x ~ "rhs")) for + binary + + */ + typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) + { + trustedStderr.writefln("Overflow on unary operator: %s%s(%s)", + x, Lhs.stringof, lhs); + return mixin(x ~ "lhs"); + } + /// ditto + typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + trustedStderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)", + Lhs.stringof, lhs, x, Rhs.stringof, rhs); + static if (x == "/") // Issue 20743: mixin below would cause SIGFPE on POSIX + return typeof(lhs / rhs).min; // or EXCEPTION_INT_OVERFLOW on Windows + else + return mixin("lhs" ~ x ~ "rhs"); + } + + // This is safe because we do not assign to the reference returned by + // `stderr`. The ability for the caller to do that is why `stderr` is not + // safe in the general case. + private @property auto ref trustedStderr() @trusted + { + import std.stdio : stderr; + + return stderr; + } +} + +/// +@safe unittest +{ + auto x = checked!Warn(42); + short x1 = cast(short) x; + //x += long(int.max); + auto y = checked!Warn(cast(const int) 42); + short y1 = cast(const byte) y; +} + +@system unittest +{ + auto a = checked!Warn(int.min); + auto b = checked!Warn(-1); + auto x = checked!Abort(int.min); + auto y = checked!Abort(-1); + + // Temporarily redirect output to stderr to make sure we get the right output. + import std.file : exists, remove; + import std.process : uniqueTempPath; + import std.stdio : stderr; + auto tmpname = uniqueTempPath; + scope(exit) if (exists(tmpname)) remove(tmpname); + auto t = stderr; + stderr.open(tmpname, "w"); + // Open a new scope to minimize code ran with stderr redirected. + { + scope(exit) stderr = t; + assert(a / b == a * b); + import std.exception : assertThrown; + import core.exception : AssertError; + assertThrown!AssertError(x / y); + } + import std.file : readText; + import std.ascii : newline; + auto witness = readText(tmpname); + auto expected = +"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline ~ +"Overflow on binary operator: int(-2147483648) * const(int)(-1)" ~ newline ~ +"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline; + assert(witness == expected, "'" ~ witness ~ "'"); +} + +// https://issues.dlang.org/show_bug.cgi?id=22249 +@safe unittest +{ + alias _ = Warn.onLowerBound!(int, int); +} + +// ProperCompare +/** + +Hook that provides arithmetically correct comparisons for equality and ordering. +Comparing an object of type $(D Checked!(X, ProperCompare)) against another +integral (for equality or ordering) ensures that no surprising conversions from +signed to unsigned integral occur before the comparison. Using $(D Checked!(X, +ProperCompare)) on either side of a comparison for equality against a +floating-point number makes sure the integral can be properly converted to the +floating point type, thus making sure equality is transitive. + +*/ +struct ProperCompare +{ + /** + Hook for `==` and `!=` that ensures comparison against integral values has + the behavior expected by the usual arithmetic rules. The built-in semantics + yield surprising behavior when comparing signed values against unsigned + values for equality, for example $(D uint.max == -1) or $(D -1_294_967_296 == + 3_000_000_000u). The call $(D hookOpEquals(x, y)) returns `true` if and only + if `x` and `y` represent the same arithmetic number. + + If one of the numbers is an integral and the other is a floating-point + number, $(D hookOpEquals(x, y)) returns `true` if and only if the integral + can be converted exactly (without approximation) to the floating-point + number. This is in order to preserve transitivity of equality: if $(D + hookOpEquals(x, y)) and $(D hookOpEquals(y, z)) then $(D hookOpEquals(y, + z)), in case `x`, `y`, and `z` are a mix of integral and floating-point + numbers. + + Params: + lhs = The left-hand side of the comparison for equality + rhs = The right-hand side of the comparison for equality + + Returns: + The result of the comparison, `true` if the values are equal + */ + static bool hookOpEquals(L, R)(L lhs, R rhs) + { + alias C = typeof(lhs + rhs); + static if (isFloatingPoint!C) + { + static if (!isFloatingPoint!L) + { + return hookOpEquals(rhs, lhs); + } + else static if (!isFloatingPoint!R) + { + static assert(isFloatingPoint!L && !isFloatingPoint!R); + auto rhs1 = C(rhs); + return lhs == rhs1 && cast(R) rhs1 == rhs; + } + else + return lhs == rhs; + } + else + { + bool error; + auto result = opChecked!"=="(lhs, rhs, error); + if (error) + { + // Only possible error is a wrong "true" + return false; + } + return result; + } + } + + /** + Hook for `<`, `<=`, `>`, and `>=` that ensures comparison against integral + values has the behavior expected by the usual arithmetic rules. The built-in + semantics yield surprising behavior when comparing signed values against + unsigned values, for example $(D 0u < -1). The call $(D hookOpCmp(x, y)) + returns `-1` if and only if `x` is smaller than `y` in abstract arithmetic + sense. + + If one of the numbers is an integral and the other is a floating-point + number, $(D hookOpEquals(x, y)) returns a floating-point number that is `-1` + if `x < y`, `0` if `x == y`, `1` if `x > y`, and `NaN` if the floating-point + number is `NaN`. + + Params: + lhs = The left-hand side of the comparison for ordering + rhs = The right-hand side of the comparison for ordering + + Returns: + The result of the comparison (negative if $(D lhs < rhs), positive if $(D + lhs > rhs), `0` if the values are equal) + */ + static auto hookOpCmp(L, R)(L lhs, R rhs) + { + alias C = typeof(lhs + rhs); + static if (isFloatingPoint!C) + { + return lhs < rhs + ? C(-1) + : lhs > rhs ? C(1) : lhs == rhs ? C(0) : C.init; + } + else + { + static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) + { + static assert(isUnsigned!C); + static assert(isUnsigned!L != isUnsigned!R); + if (!isUnsigned!L && lhs < 0) + return -1; + if (!isUnsigned!R && rhs < 0) + return 1; + } + return lhs < rhs ? -1 : lhs > rhs; + } + } +} + +/// +@safe unittest +{ + alias opEqualsProper = ProperCompare.hookOpEquals; + assert(opEqualsProper(42, 42)); + assert(opEqualsProper(42.0, 42.0)); + assert(opEqualsProper(42u, 42)); + assert(opEqualsProper(42, 42u)); + assert(-1 == 4294967295u); + assert(!opEqualsProper(-1, 4294967295u)); + assert(!opEqualsProper(const uint(-1), -1)); + assert(!opEqualsProper(uint(-1), -1.0)); + assert(3_000_000_000U == -1_294_967_296); + assert(!opEqualsProper(3_000_000_000U, -1_294_967_296)); +} + +@safe unittest +{ + alias opCmpProper = ProperCompare.hookOpCmp; + assert(opCmpProper(42, 42) == 0); + assert(opCmpProper(42, 42.0) == 0); + assert(opCmpProper(41, 42.0) < 0); + assert(opCmpProper(42, 41.0) > 0); + import std.math.traits : isNaN; + assert(isNaN(opCmpProper(41, double.init))); + assert(opCmpProper(42u, 42) == 0); + assert(opCmpProper(42, 42u) == 0); + assert(opCmpProper(-1, uint(-1)) < 0); + assert(opCmpProper(uint(-1), -1) > 0); + assert(opCmpProper(-1.0, -1) == 0); +} + +@safe unittest +{ + auto x1 = Checked!(uint, ProperCompare)(42u); + assert(x1.get < -1); + assert(x1 > -1); +} + +// WithNaN +/** + +Hook that reserves a special value as a "Not a Number" representative. For +signed integrals, the reserved value is `T.min`. For signed integrals, the +reserved value is `T.max`. + +The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must +be taken that all variables are explicitly initialized. Any arithmetic and logic +operation involving at least on NaN becomes NaN itself. All of $(D a == b), $(D +a < b), $(D a > b), $(D a <= b), $(D a >= b) yield `false` if at least one of +`a` and `b` is NaN. + +*/ +struct WithNaN +{ +static: + /** + The default value used for values not explicitly initialized. It is the NaN + value, i.e. `T.min` for signed integrals and `T.max` for unsigned integrals. + */ + enum T defaultValue(T) = T.min == 0 ? T.max : T.min; + /** + The maximum value representable is `T.max` for signed integrals, $(D + T.max - 1) for unsigned integrals. The minimum value representable is $(D + T.min + 1) for signed integrals, `0` for unsigned integrals. + */ + enum T max(T) = cast(T) (T.min == 0 ? T.max - 1 : T.max); + /// ditto + enum T min(T) = cast(T) (T.min == 0 ? T(0) : T.min + 1); + + /** + If `rhs` is `WithNaN.defaultValue!Rhs`, returns + `WithNaN.defaultValue!Lhs`. Otherwise, returns $(D cast(Lhs) rhs). + + Params: + rhs = the value being cast (`Rhs` is the first argument to `Checked`) + Lhs = the target type of the cast + + Returns: The result of the cast operation. + */ + Lhs hookOpCast(Lhs, Rhs)(Rhs rhs) + { + static if (is(Lhs == bool)) + { + return rhs != defaultValue!Rhs && rhs != 0; + } + else static if (valueConvertible!(Rhs, Lhs)) + { + return rhs != defaultValue!Rhs ? Lhs(rhs) : defaultValue!Lhs; + } + else + { + // Not value convertible, only viable option is rhs fits within the + // bounds of Lhs + static if (ProperCompare.hookOpCmp(Rhs.min, Lhs.min) < 0) + { + // Example: hookOpCast!short(int(42)), hookOpCast!uint(int(42)) + if (ProperCompare.hookOpCmp(rhs, Lhs.min) < 0) + return defaultValue!Lhs; + } + static if (ProperCompare.hookOpCmp(Rhs.max, Lhs.max) > 0) + { + // Example: hookOpCast!int(uint(42)) + if (ProperCompare.hookOpCmp(rhs, Lhs.max) > 0) + return defaultValue!Lhs; + } + return cast(Lhs) rhs; + } + } + + /// + @safe unittest + { + auto x = checked!WithNaN(422); + assert((cast(ubyte) x) == 255); + x = checked!WithNaN(-422); + assert((cast(byte) x) == -128); + assert(cast(short) x == -422); + assert(cast(bool) x); + x = x.init; // set back to NaN + assert(x != true); + assert(x != false); + } + + /** + + Returns `false` if $(D lhs == WithNaN.defaultValue!Lhs), $(D lhs == rhs) + otherwise. + + Params: + lhs = The left-hand side of the comparison (`Lhs` is the first argument to + `Checked`) + rhs = The right-hand side of the comparison + + Returns: `lhs != WithNaN.defaultValue!Lhs && lhs == rhs` + */ + bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + return lhs != defaultValue!Lhs && lhs == rhs; + } + + /** + + If $(D lhs == WithNaN.defaultValue!Lhs), returns `double.init`. Otherwise, + has the same semantics as the default comparison. + + Params: + lhs = The left-hand side of the comparison (`Lhs` is the first argument to + `Checked`) + rhs = The right-hand side of the comparison + + Returns: `double.init` if `lhs == WitnNaN.defaultValue!Lhs`, `-1.0` if $(D + lhs < rhs), `0.0` if $(D lhs == rhs), `1.0` if $(D lhs > rhs). + + */ + double hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + if (lhs == defaultValue!Lhs) return double.init; + return lhs < rhs + ? -1.0 + : lhs > rhs ? 1.0 : lhs == rhs ? 0.0 : double.init; + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + assert(!(x < 0) && !(x > 0) && !(x == 0)); + x = 1; + assert(x > 0 && !(x < 0) && !(x == 0)); + } + + /** + Defines hooks for unary operators `-`, `~`, `++`, and `--`. + + For `-` and `~`, if $(D v == WithNaN.defaultValue!T), returns + `WithNaN.defaultValue!T`. Otherwise, the semantics is the same as for the + built-in operator. + + For `++` and `--`, if $(D v == WithNaN.defaultValue!Lhs) or the operation + would result in an overflow, sets `v` to `WithNaN.defaultValue!T`. + Otherwise, the semantics is the same as for the built-in operator. + + Params: + x = The operator symbol + v = The left-hand side of the comparison (`T` is the first argument to + `Checked`) + + Returns: $(UL $(LI For $(D x == "-" || x == "~"): If $(D v == + WithNaN.defaultValue!T), the function returns `WithNaN.defaultValue!T`. + Otherwise it returns the normal result of the operator.) $(LI For $(D x == + "++" || x == "--"): The function returns `void`.)) + + */ + auto hookOpUnary(string x, T)(ref T v) + { + static if (x == "-" || x == "~") + { + return v != defaultValue!T ? mixin(x ~ "v") : v; + } + else static if (x == "++") + { + static if (defaultValue!T == T.min) + { + if (v != defaultValue!T) + { + if (v == T.max) v = defaultValue!T; + else ++v; + } + } + else + { + static assert(defaultValue!T == T.max); + if (v != defaultValue!T) ++v; + } + } + else static if (x == "--") + { + if (v != defaultValue!T) --v; + } + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + ++x; + assert(x.isNaN); + x = 1; + assert(!x.isNaN); + x = -x; + ++x; + assert(!x.isNaN); + } + + @safe unittest // for coverage + { + Checked!(uint, WithNaN) y; + ++y; + assert(y.isNaN); + } + + /** + Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, + `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the + left-hand side operand. If $(D lhs == WithNaN.defaultValue!Lhs), returns + $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the + operand. Otherwise, evaluates the operand. If evaluation does not overflow, + returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + + rhs))). + + Params: + x = The operator symbol + lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) + rhs = The right-hand side operand + + Returns: If $(D lhs != WithNaN.defaultValue!Lhs) and the operator does not + overflow, the function returns the same result as the built-in operator. In + all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). + */ + auto hookOpBinary(string x, L, R)(L lhs, R rhs) + { + alias Result = typeof(lhs + rhs); + if (lhs != defaultValue!L) + { + bool error; + auto result = opChecked!x(lhs, rhs, error); + if (!error) return result; + } + return defaultValue!Result; + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + assert((x + 1).isNaN); + x = 100; + assert(!(x + 1).isNaN); + } + + /** + Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, + `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the + right-hand side operand. If $(D rhs == WithNaN.defaultValue!Rhs), returns + $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the + operand. Otherwise, evaluates the operand. If evaluation does not overflow, + returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + + rhs))). + + Params: + x = The operator symbol + lhs = The left-hand side operand + rhs = The right-hand side operand (`Rhs` is the first argument to `Checked`) + + Returns: If $(D rhs != WithNaN.defaultValue!Rhs) and the operator does not + overflow, the function returns the same result as the built-in operator. In + all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). + */ + auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) + { + alias Result = typeof(lhs + rhs); + if (rhs != defaultValue!R) + { + bool error; + auto result = opChecked!x(lhs, rhs, error); + if (!error) return result; + } + return defaultValue!Result; + } + /// + @safe unittest + { + Checked!(int, WithNaN) x; + assert((1 + x).isNaN); + x = 100; + assert(!(1 + x).isNaN); + } + + /** + + Defines hooks for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, + `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` for cases where a `Checked` + object is the left-hand side operand. If $(D lhs == + WithNaN.defaultValue!Lhs), no action is carried. Otherwise, evaluates the + operand. If evaluation does not overflow and fits in `Lhs` without loss of + information or change of sign, sets `lhs` to the result. Otherwise, sets + `lhs` to `WithNaN.defaultValue!Lhs`. + + Params: + x = The operator symbol (without the `=`) + lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) + rhs = The right-hand side operand + + Returns: `void` + */ + void hookOpOpAssign(string x, L, R)(ref L lhs, R rhs) + { + if (lhs == defaultValue!L) + return; + bool error; + auto temp = opChecked!x(lhs, rhs, error); + lhs = error + ? defaultValue!L + : hookOpCast!L(temp); + } + + /// + @safe unittest + { + Checked!(int, WithNaN) x; + x += 4; + assert(x.isNaN); + x = 0; + x += 4; + assert(!x.isNaN); + x += int.max; + assert(x.isNaN); + } +} + +/// +@safe unittest +{ + auto x1 = Checked!(int, WithNaN)(); + assert(x1.isNaN); + assert(x1.get == int.min); + assert(x1 != x1); + assert(!(x1 < x1)); + assert(!(x1 > x1)); + assert(!(x1 == x1)); + ++x1; + assert(x1.isNaN); + assert(x1.get == int.min); + --x1; + assert(x1.isNaN); + assert(x1.get == int.min); + x1 = 42; + assert(!x1.isNaN); + assert(x1 == x1); + assert(x1 <= x1); + assert(x1 >= x1); + static assert(x1.min == int.min + 1); + x1 += long(int.max); +} + +/** +Queries whether a $(D Checked!(T, WithNaN)) object is not a number (NaN). + +Params: + x = the `Checked` instance queried + +Returns: + `true` if `x` is a NaN, `false` otherwise +*/ +bool isNaN(T)(const Checked!(T, WithNaN) x) +{ + return x.get == x.init.get; +} + +/// +@safe unittest +{ + auto x1 = Checked!(int, WithNaN)(); + assert(x1.isNaN); + x1 = 1; + assert(!x1.isNaN); + x1 = x1.init; + assert(x1.isNaN); +} + +@safe unittest +{ + void test1(T)() + { + auto x1 = Checked!(T, WithNaN)(); + assert(x1.isNaN); + assert(x1.get == int.min); + assert(x1 != x1); + assert(!(x1 < x1)); + assert(!(x1 > x1)); + assert(!(x1 == x1)); + assert(x1.get == int.min); + auto x2 = Checked!(T, WithNaN)(42); + assert(!x2.isNaN); + assert(x2 == x2); + assert(x2 <= x2); + assert(x2 >= x2); + static assert(x2.min == T.min + 1); + } + test1!int; + test1!(const int); + test1!(immutable int); + + void test2(T)() + { + auto x1 = Checked!(T, WithNaN)(); + assert(x1.get == T.min); + assert(x1 != x1); + assert(!(x1 < x1)); + assert(!(x1 > x1)); + assert(!(x1 == x1)); + ++x1; + assert(x1.get == T.min); + --x1; + assert(x1.get == T.min); + x1 = 42; + assert(x1 == x1); + assert(x1 <= x1); + assert(x1 >= x1); + static assert(x1.min == T.min + 1); + x1 += long(T.max); + } + test2!int; +} + +@safe unittest +{ + alias Smart(T) = Checked!(Checked!(T, ProperCompare), WithNaN); + Smart!int x1; + assert(x1 != x1); + x1 = -1; + assert(x1 < 1u); + auto x2 = Smart!(const int)(42); +} + +// Saturate +/** + +Hook that implements $(I saturation), i.e. any arithmetic operation that would +overflow leaves the result at its extreme value (`min` or `max` depending on the +direction of the overflow). + +Saturation is not sticky; if a value reaches its saturation value, another +operation may take it back to normal range. + +*/ +struct Saturate +{ +static: + /** + + Implements saturation for operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, + and `>>>=`. This hook is called if the result of the binary operation does + not fit in `Lhs` without loss of information or a change in sign. + + Params: + Rhs = The right-hand side type in the assignment, after the operation has + been computed + bound = The bound being violated + + Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise. + + */ + T onLowerBound(Rhs, T)(Rhs, T bound) + { + return bound; + } + /// ditto + T onUpperBound(Rhs, T)(Rhs, T bound) + { + return bound; + } + /// + @safe unittest + { + auto x = checked!Saturate(short(100)); + x += 33000; + assert(x == short.max); + x -= 70000; + assert(x == short.min); + } + + /** + + Implements saturation for operators `+`, `-` (unary and binary), `*`, `/`, + `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`. + + For unary `-`, `onOverflow` is called if $(D lhs == Lhs.min) and `Lhs` is a + signed type. The function returns `Lhs.max`. + + For binary operators, the result is as follows: $(UL $(LI `Lhs.max` if the + result overflows in the positive direction, on division by `0`, or on + shifting right by a negative value) $(LI `Lhs.min` if the result overflows + in the negative direction) $(LI `0` if `lhs` is being shifted left by a + negative value, or shifted right by a large positive value)) + + Params: + x = The operator involved in the `opAssign` operation + Lhs = The left-hand side type of the operator (`Lhs` is the first argument to + `Checked`) + Rhs = The right-hand side type in the operator + + Returns: The saturated result of the operator. + + */ + auto onOverflow(string x, Lhs)(Lhs) + { + static assert(x == "-" || x == "++" || x == "--"); + return x == "--" ? Lhs.min : Lhs.max; + } + /// ditto + typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + static if (x == "+") + return rhs >= 0 ? Lhs.max : Lhs.min; + else static if (x == "*") + return (lhs >= 0) == (rhs >= 0) ? Lhs.max : Lhs.min; + else static if (x == "^^") + return lhs > 0 || !(rhs & 1) ? Lhs.max : Lhs.min; + else static if (x == "-") + return rhs >= 0 ? Lhs.min : Lhs.max; + else static if (x == "/" || x == "%") + return Lhs.max; + else static if (x == "<<") + return rhs >= 0 ? Lhs.max : 0; + else static if (x == ">>" || x == ">>>") + return rhs >= 0 ? 0 : Lhs.max; + else + static assert(false); + } + /// + @safe unittest + { + assert(checked!Saturate(int.max) + 1 == int.max); + assert(checked!Saturate(100) ^^ 10 == int.max); + assert(checked!Saturate(-100) ^^ 10 == int.max); + assert(checked!Saturate(100) / 0 == int.max); + assert(checked!Saturate(100) << -1 == 0); + assert(checked!Saturate(100) << 33 == int.max); + assert(checked!Saturate(100) >> -1 == int.max); + assert(checked!Saturate(100) >> 33 == 0); + } +} + +/// +@safe unittest +{ + auto x = checked!Saturate(int.max); + ++x; + assert(x == int.max); + --x; + assert(x == int.max - 1); + x = int.min; + assert(-x == int.max); + x -= 42; + assert(x == int.min); + assert(x * -2 == int.max); +} + +/* +Yields `true` if `T1` is "value convertible" (by C's "value preserving" rule, +see $(HTTP c-faq.com/expr/preservingrules.html)) to `T2`, where the two are +integral types. That is, all of values in `T1` are also in `T2`. For example +`int` is value convertible to `long` but not to `uint` or `ulong`. +*/ +private enum valueConvertible(T1, T2) = isIntegral!T1 && isIntegral!T2 && + is(T1 : T2) && ( + isUnsigned!T1 == isUnsigned!T2 || // same signedness + !isUnsigned!T2 && T2.sizeof > T1.sizeof // safely convertible + ); + +/** + +Defines binary operations with overflow checking for any two integral types. +The result type obeys the language rules (even when they may be +counterintuitive), and `overflow` is set if an overflow occurs (including +inadvertent change of signedness, e.g. `-1` is converted to `uint`). +Conceptually the behavior is: + +$(OL $(LI Perform the operation in infinite precision) +$(LI If the infinite-precision result fits in the result type, return it and +do not touch `overflow`) +$(LI Otherwise, set `overflow` to `true` and return an unspecified value) +) + +The implementation exploits properties of types and operations to minimize +additional work. + +Params: +x = The binary operator involved, e.g. `/` +lhs = The left-hand side of the operator +rhs = The right-hand side of the operator +overflow = The overflow indicator (assigned `true` in case there's an error) + +Returns: +The result of the operation, which is the same as the built-in operator +*/ +typeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()"))) +opChecked(string x, L, R)(const L lhs, const R rhs, ref bool overflow) +if (isIntegral!L && isIntegral!R) +{ + static if (x == "cmp") + alias Result = int; + else + alias Result = typeof(mixin("L() " ~ x ~ " R()")); + + import core.checkedint : addu, adds, subs, muls, subu, mulu; + import std.algorithm.comparison : among; + static if (x == "==") + { + alias C = typeof(lhs + rhs); + static if (valueConvertible!(L, C) && valueConvertible!(R, C)) + { + // Values are converted to R before comparison, cool. + return lhs == rhs; + } + else + { + static assert(isUnsigned!C); + static assert(isUnsigned!L != isUnsigned!R); + if (lhs != rhs) return false; + // R(lhs) and R(rhs) have the same bit pattern, yet may be + // different due to signedness change. + static if (!isUnsigned!R) + { + if (rhs >= 0) + return true; + } + else + { + if (lhs >= 0) + return true; + } + overflow = true; + return true; + } + } + else static if (x == "cmp") + { + alias C = typeof(lhs + rhs); + static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) + { + static assert(isUnsigned!C); + static assert(isUnsigned!L != isUnsigned!R); + if (!isUnsigned!L && lhs < 0) + { + overflow = true; + return -1; + } + if (!isUnsigned!R && rhs < 0) + { + overflow = true; + return 1; + } + } + return lhs < rhs ? -1 : lhs > rhs; + } + else static if (x.among("<<", ">>", ">>>")) + { + // Handle shift separately from all others. The test below covers + // negative rhs as well. + import std.conv : unsigned; + if (unsigned(rhs) > 8 * Result.sizeof) goto fail; + return mixin("lhs" ~ x ~ "rhs"); + } + else static if (x.among("&", "|", "^")) + { + // Nothing to check + return mixin("lhs" ~ x ~ "rhs"); + } + else static if (x == "^^") + { + // Exponentiation is weird, handle separately + return pow(lhs, rhs, overflow); + } + else static if (valueConvertible!(L, Result) && + valueConvertible!(R, Result)) + { + static if (L.sizeof < Result.sizeof && R.sizeof < Result.sizeof && + x.among("+", "-", "*")) + { + // No checks - both are value converted and result is in range + return mixin("lhs" ~ x ~ "rhs"); + } + else static if (x == "+") + { + static if (isUnsigned!Result) alias impl = addu; + else alias impl = adds; + return impl(Result(lhs), Result(rhs), overflow); + } + else static if (x == "-") + { + static if (isUnsigned!Result) alias impl = subu; + else alias impl = subs; + return impl(Result(lhs), Result(rhs), overflow); + } + else static if (x == "*") + { + static if (!isUnsigned!L && !isUnsigned!R && + is(L == Result)) + { + if (lhs == Result.min && rhs == -1) goto fail; + } + static if (isUnsigned!Result) alias impl = mulu; + else alias impl = muls; + return impl(Result(lhs), Result(rhs), overflow); + } + else static if (x == "/" || x == "%") + { + static if (!isUnsigned!L && !isUnsigned!R && + is(L == Result) && x == "/") + { + if (lhs == Result.min && rhs == -1) goto fail; + } + if (rhs == 0) goto fail; + return mixin("lhs" ~ x ~ "rhs"); + } + else static assert(0, x); + } + else // Mixed signs + { + static assert(isUnsigned!Result); + static assert(isUnsigned!L != isUnsigned!R); + static if (x == "+") + { + static if (!isUnsigned!L) + { + if (lhs < 0) + return subu(Result(rhs), Result(-lhs), overflow); + } + else static if (!isUnsigned!R) + { + if (rhs < 0) + return subu(Result(lhs), Result(-rhs), overflow); + } + return addu(Result(lhs), Result(rhs), overflow); + } + else static if (x == "-") + { + static if (!isUnsigned!L) + { + if (lhs < 0) goto fail; + } + else static if (!isUnsigned!R) + { + if (rhs < 0) + return addu(Result(lhs), Result(-rhs), overflow); + } + return subu(Result(lhs), Result(rhs), overflow); + } + else static if (x == "*") + { + static if (!isUnsigned!L) + { + if (lhs < 0) goto fail; + } + else static if (!isUnsigned!R) + { + if (rhs < 0) goto fail; + } + return mulu(Result(lhs), Result(rhs), overflow); + } + else static if (x == "/" || x == "%") + { + static if (!isUnsigned!L) + { + if (lhs < 0 || rhs == 0) goto fail; + } + else static if (!isUnsigned!R) + { + if (rhs <= 0) goto fail; + } + return mixin("Result(lhs)" ~ x ~ "Result(rhs)"); + } + else static assert(0, x); + } + debug assert(false); +fail: + overflow = true; + return Result(0); +} + +/// +@safe unittest +{ + bool overflow; + assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow); + assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow); + assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow); + assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow); + assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow); +} + +/// +@safe unittest +{ + bool overflow; + assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow); + assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow); + assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow); + assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow); +} + +@safe unittest +{ + bool overflow; + assert(opChecked!"*"(2, 3, overflow) == 6 && !overflow); + assert(opChecked!"*"(2, 3u, overflow) == 6 && !overflow); + assert(opChecked!"*"(1u, -1, overflow) == 0 && overflow); + //assert(mul(-1, 1u, overflow) == uint.max - 1 && overflow); +} + +@safe unittest +{ + bool overflow; + assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); + assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); + assert(opChecked!"/"(6u, 3, overflow) == 2 && !overflow); + assert(opChecked!"/"(6, 3u, overflow) == 2 && !overflow); + assert(opChecked!"/"(11, 0, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"/"(6u, 0, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"/"(-6, 2u, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"/"(-6, 0u, overflow) == 0 && overflow); + overflow = false; + assert(opChecked!"cmp"(0u, -6, overflow) == 1 && overflow); + overflow = false; + assert(opChecked!"|"(1, 2, overflow) == 3 && !overflow); +} + +/* +Exponentiation function used by the implementation of operator `^^`. +*/ +private pure @safe nothrow @nogc +auto pow(L, R)(const L lhs, const R rhs, ref bool overflow) +if (isIntegral!L && isIntegral!R) +{ + if (rhs <= 1) + { + if (rhs == 0) return 1; + static if (!isUnsigned!R) + return rhs == 1 + ? lhs + : (rhs == -1 && (lhs == 1 || lhs == -1)) ? lhs : 0; + else + return lhs; + } + + typeof(lhs ^^ rhs) b = void; + static if (!isUnsigned!L && isUnsigned!(typeof(b))) + { + // Need to worry about mixed-sign stuff + if (lhs < 0) + { + if (rhs & 1) + { + if (lhs < 0) overflow = true; + return 0; + } + b = -lhs; + } + else + { + b = lhs; + } + } + else + { + b = lhs; + } + if (b == 1) return 1; + if (b == -1) return (rhs & 1) ? -1 : 1; + if (rhs > 63) + { + overflow = true; + return 0; + } + + assert((b > 1 || b < -1) && rhs > 1); + return powImpl(b, cast(uint) rhs, overflow); +} + +// Inspiration: http://www.stepanovpapers.com/PAM.pdf +pure @safe nothrow @nogc +private T powImpl(T)(T b, uint e, ref bool overflow) +if (isIntegral!T && T.sizeof >= 4) +{ + assert(e > 1); + + import core.checkedint : muls, mulu; + static if (isUnsigned!T) alias mul = mulu; + else alias mul = muls; + + T r = b; + --e; + // Loop invariant: r * (b ^^ e) is the actual result + for (;; e /= 2) + { + if (e % 2) + { + r = mul(r, b, overflow); + if (e == 1) break; + } + b = mul(b, b, overflow); + } + return r; +} + +@safe unittest +{ + static void testPow(T)(T x, uint e) + { + bool overflow; + assert(opChecked!"^^"(T(0), 0, overflow) == 1); + assert(opChecked!"^^"(-2, T(0), overflow) == 1); + assert(opChecked!"^^"(-2, T(1), overflow) == -2); + assert(opChecked!"^^"(-1, -1, overflow) == -1); + assert(opChecked!"^^"(-2, 1, overflow) == -2); + assert(opChecked!"^^"(-2, -1, overflow) == 0); + assert(opChecked!"^^"(-2, 4u, overflow) == 16); + assert(!overflow); + assert(opChecked!"^^"(-2, 3u, overflow) == 0); + assert(overflow); + overflow = false; + assert(opChecked!"^^"(3, 64u, overflow) == 0); + assert(overflow); + overflow = false; + foreach (uint i; 0 .. e) + { + assert(opChecked!"^^"(x, i, overflow) == x ^^ i); + assert(!overflow); + } + assert(opChecked!"^^"(x, e, overflow) == x ^^ e); + assert(overflow); + } + + testPow!int(3, 21); + testPow!uint(3, 21); + testPow!long(3, 40); + testPow!ulong(3, 41); +} + +version (StdUnittest) private struct CountOverflows +{ + uint calls; + auto onOverflow(string op, Lhs)(Lhs lhs) + { + ++calls; + return mixin(op ~ "lhs"); + } + auto onOverflow(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return mixin("lhs" ~ op ~ "rhs"); + } + T onLowerBound(Rhs, T)(Rhs rhs, T) + { + ++calls; + return cast(T) rhs; + } + T onUpperBound(Rhs, T)(Rhs rhs, T) + { + ++calls; + return cast(T) rhs; + } +} + +// opBinary +@nogc nothrow pure @safe unittest +{ + static struct CountOpBinary + { + uint calls; + auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return mixin("lhs" ~ op ~ "rhs"); + } + } + auto x = Checked!(const int, void)(42), y = Checked!(immutable int, void)(142); + assert(x + y == 184); + assert(x + 100 == 142); + assert(y - x == 100); + assert(200 - x == 158); + assert(y * x == 142 * 42); + assert(x / 1 == 42); + assert(x % 20 == 2); + + auto x1 = Checked!(int, CountOverflows)(42); + assert(x1 + 0 == 42); + assert(x1 + false == 42); + assert(is(typeof(x1 + 0.5) == double)); + assert(x1 + 0.5 == 42.5); + assert(x1.hook.calls == 0); + assert(x1 + int.max == int.max + 42); + assert(x1.hook.calls == 1); + assert(x1 * 2 == 84); + assert(x1.hook.calls == 1); + assert(x1 / 2 == 21); + assert(x1.hook.calls == 1); + assert(x1 % 20 == 2); + assert(x1.hook.calls == 1); + assert(x1 << 2 == 42 << 2); + assert(x1.hook.calls == 1); + assert(x1 << 42 == x1.get << x1.get); + assert(x1.hook.calls == 2); + x1 = int.min; + assert(x1 - 1 == int.max); + assert(x1.hook.calls == 3); + + auto x2 = Checked!(int, CountOpBinary)(42); + assert(x2 + 1 == 43); + assert(x2.hook.calls == 1); + + auto x3 = Checked!(uint, CountOverflows)(42u); + assert(x3 + 1 == 43); + assert(x3.hook.calls == 0); + assert(x3 - 1 == 41); + assert(x3.hook.calls == 0); + assert(x3 + (-42) == 0); + assert(x3.hook.calls == 0); + assert(x3 - (-42) == 84); + assert(x3.hook.calls == 0); + assert(x3 * 2 == 84); + assert(x3.hook.calls == 0); + assert(x3 * -2 == -84); + assert(x3.hook.calls == 1); + assert(x3 / 2 == 21); + assert(x3.hook.calls == 1); + assert(x3 / -2 == 0); + assert(x3.hook.calls == 2); + assert(x3 ^^ 2 == 42 * 42); + assert(x3.hook.calls == 2); + + auto x4 = Checked!(int, CountOverflows)(42); + assert(x4 + 1 == 43); + assert(x4.hook.calls == 0); + assert(x4 + 1u == 43); + assert(x4.hook.calls == 0); + assert(x4 - 1 == 41); + assert(x4.hook.calls == 0); + assert(x4 * 2 == 84); + assert(x4.hook.calls == 0); + x4 = -2; + assert(x4 + 2u == 0); + assert(x4.hook.calls == 0); + assert(x4 * 2u == -4); + assert(x4.hook.calls == 1); + + auto x5 = Checked!(int, CountOverflows)(3); + assert(x5 ^^ 0 == 1); + assert(x5 ^^ 1 == 3); + assert(x5 ^^ 2 == 9); + assert(x5 ^^ 3 == 27); + assert(x5 ^^ 4 == 81); + assert(x5 ^^ 5 == 81 * 3); + assert(x5 ^^ 6 == 81 * 9); +} + +// opBinaryRight +@nogc nothrow pure @safe unittest +{ + auto x1 = Checked!(int, CountOverflows)(42); + assert(1 + x1 == 43); + assert(true + x1 == 43); + assert(0.5 + x1 == 42.5); + auto x2 = Checked!(int, void)(42); + assert(x1 + x2 == 84); + assert(x2 + x1 == 84); +} + +// opOpAssign +@safe unittest +{ + auto x1 = Checked!(int, CountOverflows)(3); + assert((x1 += 2) == 5); + x1 *= 2_000_000_000L; + assert(x1.hook.calls == 1); + x1 *= -2_000_000_000L; + assert(x1.hook.calls == 2); + + auto x2 = Checked!(ushort, CountOverflows)(ushort(3)); + assert((x2 += 2) == 5); + assert(x2.hook.calls == 0); + assert((x2 += ushort.max) == cast(ushort) (ushort(5) + ushort.max)); + assert(x2.hook.calls == 1); + + auto x3 = Checked!(uint, CountOverflows)(3u); + x3 *= ulong(2_000_000_000); + assert(x3.hook.calls == 1); +} + +// opAssign +@safe unittest +{ + Checked!(int, void) x; + x = 42; + assert(x.get == 42); + x = x; + assert(x.get == 42); + x = short(43); + assert(x.get == 43); + x = ushort(44); + assert(x.get == 44); +} + +@safe unittest +{ + static assert(!is(typeof(Checked!(short, void)(ushort(42))))); + static assert(!is(typeof(Checked!(int, void)(long(42))))); + static assert(!is(typeof(Checked!(int, void)(ulong(42))))); + assert(Checked!(short, void)(short(42)).get == 42); + assert(Checked!(int, void)(ushort(42)).get == 42); +} + +// opCast +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof(cast(float) Checked!(int, void)(42)) == float)); + assert(cast(float) Checked!(int, void)(42) == 42); + + assert(is(typeof(cast(long) Checked!(int, void)(42)) == long)); + assert(cast(long) Checked!(int, void)(42) == 42); + static assert(is(typeof(cast(long) Checked!(uint, void)(42u)) == long)); + assert(cast(long) Checked!(uint, void)(42u) == 42); + + auto x = Checked!(int, void)(42); + if (x) {} else assert(0); + x = 0; + if (x) assert(0); + + static struct Hook1 + { + uint calls; + Dst hookOpCast(Dst, Src)(Src value) + { + ++calls; + return 42; + } + } + auto y = Checked!(long, Hook1)(long.max); + assert(cast(int) y == 42); + assert(cast(uint) y == 42); + assert(y.hook.calls == 2); + + static struct Hook2 + { + uint calls; + Dst onBadCast(Dst, Src)(Src value) + { + ++calls; + return 42; + } + } + auto x1 = Checked!(uint, Hook2)(100u); + assert(cast(ushort) x1 == 100); + assert(cast(short) x1 == 100); + assert(cast(float) x1 == 100); + assert(cast(double) x1 == 100); + assert(cast(real) x1 == 100); + assert(x1.hook.calls == 0); + assert(cast(int) x1 == 100); + assert(x1.hook.calls == 0); + x1 = uint.max; + assert(cast(int) x1 == 42); + assert(x1.hook.calls == 1); + + auto x2 = Checked!(int, Hook2)(-100); + assert(cast(short) x2 == -100); + assert(cast(ushort) x2 == 42); + assert(cast(uint) x2 == 42); + assert(cast(ulong) x2 == 42); + assert(x2.hook.calls == 3); +} + +// opEquals +@nogc nothrow pure @safe unittest +{ + assert(Checked!(int, void)(42) == 42L); + assert(42UL == Checked!(int, void)(42)); + + static struct Hook1 + { + uint calls; + bool hookOpEquals(Lhs, Rhs)(const Lhs lhs, const Rhs rhs) + { + ++calls; + return lhs != rhs; + } + } + auto x1 = Checked!(int, Hook1)(100); + assert(x1 != Checked!(long, Hook1)(100)); + assert(x1.hook.calls == 1); + assert(x1 != 100u); + assert(x1.hook.calls == 2); + + static struct Hook2 + { + uint calls; + bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return false; + } + } + auto x2 = Checked!(int, Hook2)(-100); + assert(x2 != x1); + // For coverage: lhs has no hookOpEquals, rhs does + assert(Checked!(uint, void)(100u) != x2); + // For coverage: different types, neither has a hookOpEquals + assert(Checked!(uint, void)(100u) == Checked!(int, void*)(100)); + assert(x2.hook.calls == 0); + assert(x2 != -100); + assert(x2.hook.calls == 1); + assert(x2 != cast(uint) -100); + assert(x2.hook.calls == 2); + x2 = 100; + assert(x2 != cast(uint) 100); + assert(x2.hook.calls == 3); + x2 = -100; + + auto x3 = Checked!(uint, Hook2)(100u); + assert(x3 != 100); + x3 = uint.max; + assert(x3 != -1); + + assert(x2 != x3); +} + +// opCmp +@nogc nothrow pure @safe unittest +{ + Checked!(int, void) x; + assert(x <= x); + assert(x < 45); + assert(x < 45u); + assert(x > -45); + assert(x < 44.2); + assert(x > -44.2); + assert(!(x < double.init)); + assert(!(x > double.init)); + assert(!(x <= double.init)); + assert(!(x >= double.init)); + + static struct Hook1 + { + uint calls; + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return 0; + } + } + auto x1 = Checked!(int, Hook1)(42); + assert(!(x1 < 43u)); + assert(!(43u < x1)); + assert(x1.hook.calls == 2); + + static struct Hook2 + { + uint calls; + int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) + { + ++calls; + return ProperCompare.hookOpCmp(lhs, rhs); + } + } + auto x2 = Checked!(int, Hook2)(-42); + assert(x2 < 43u); + assert(43u > x2); + assert(x2.hook.calls == 2); + x2 = 42; + assert(x2 > 41u); + + auto x3 = Checked!(uint, Hook2)(42u); + assert(x3 > 41); + assert(x3 > -41); +} + +// opUnary +@nogc nothrow pure @safe unittest +{ + auto x = Checked!(int, void)(42); + assert(x == +x); + static assert(is(typeof(-x) == typeof(x))); + assert(-x == Checked!(int, void)(-42)); + static assert(is(typeof(~x) == typeof(x))); + assert(~x == Checked!(int, void)(~42)); + assert(++x == 43); + assert(--x == 42); + + static struct Hook1 + { + uint calls; + auto hookOpUnary(string op, T)(T value) if (op == "-") + { + ++calls; + return T(42); + } + auto hookOpUnary(string op, T)(T value) if (op == "~") + { + ++calls; + return T(43); + } + } + auto x1 = Checked!(int, Hook1)(100); + assert(is(typeof(-x1) == typeof(x1))); + assert(-x1 == Checked!(int, Hook1)(42)); + assert(is(typeof(~x1) == typeof(x1))); + assert(~x1 == Checked!(int, Hook1)(43)); + assert(x1.hook.calls == 2); + + static struct Hook2 + { + uint calls; + void hookOpUnary(string op, T)(ref T value) if (op == "++") + { + ++calls; + --value; + } + void hookOpUnary(string op, T)(ref T value) if (op == "--") + { + ++calls; + ++value; + } + } + auto x2 = Checked!(int, Hook2)(100); + assert(++x2 == 99); + assert(x2 == 99); + assert(--x2 == 100); + assert(x2 == 100); + + auto x3 = Checked!(int, CountOverflows)(int.max - 1); + assert(++x3 == int.max); + assert(x3.hook.calls == 0); + assert(++x3 == int.min); + assert(x3.hook.calls == 1); + assert(-x3 == int.min); + assert(x3.hook.calls == 2); + + x3 = int.min + 1; + assert(--x3 == int.min); + assert(x3.hook.calls == 2); + assert(--x3 == int.max); + assert(x3.hook.calls == 3); +} + +// +@nogc nothrow pure @safe unittest +{ + Checked!(int, void) x; + assert(x == x); + assert(x == +x); + assert(x == -x); + ++x; + assert(x == 1); + x++; + assert(x == 2); + + x = 42; + assert(x == 42); + const short _short = 43; + x = _short; + assert(x == _short); + ushort _ushort = 44; + x = _ushort; + assert(x == _ushort); + assert(x == 44.0); + assert(x != 44.1); + assert(x < 45); + assert(x < 44.2); + assert(x > -45); + assert(x > -44.2); + + assert(cast(long) x == 44); + assert(cast(short) x == 44); + + const Checked!(uint, void) y; + assert(y <= y); + assert(y == 0); + assert(y < x); + x = -1; + assert(x > y); +} + +@nogc nothrow pure @safe unittest +{ + alias cint = Checked!(int, void); + cint a = 1, b = 2; + a += b; + assert(a == cint(3)); + + alias ccint = Checked!(cint, Saturate); + ccint c = 14; + a += c; + assert(a == cint(17)); +} + +// toHash +@safe unittest +{ + assert(checked(42).toHash() == checked(42).toHash()); + assert(checked(12).toHash() != checked(19).toHash()); + + static struct Hook1 + { + static size_t hookToHash(T)(T payload) nothrow @trusted + { + static if (size_t.sizeof == 4) + { + return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF; + } + else + { + return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF_FFFF_FFFF; + } + + } + } + + auto a = checked!Hook1(78); + auto b = checked!Hook1(78); + assert(a.toHash() == b.toHash()); + + assert(checked!Hook1(12).toHash() != checked!Hook1(13).toHash()); + + static struct Hook2 + { + static if (size_t.sizeof == 4) + { + static size_t hashMask = 0xFFFF_0000; + } + else + { + static size_t hashMask = 0xFFFF_0000_FFFF_0000; + } + + static size_t hookToHash(T)(T payload) nothrow @trusted + { + return typeid(payload).getHash(&payload) ^ hashMask; + } + } + + auto x = checked!Hook2(1901); + auto y = checked!Hook2(1989); + + assert((() nothrow @safe => x.toHash() == x.toHash())()); + + assert(x.toHash() == x.toHash()); + assert(x.toHash() != y.toHash()); + assert(checked!Hook1(1901).toHash() != x.toHash()); + + immutable z = checked!Hook1(1901); + immutable t = checked!Hook1(1901); + immutable w = checked!Hook2(1901); + + assert(z.toHash() == t.toHash()); + assert(z.toHash() != x.toHash()); + assert(z.toHash() != w.toHash()); + + const long c = 0xF0F0F0F0; + const long d = 0xF0F0F0F0; + + assert(checked!Hook1(c).toHash() != checked!Hook2(c)); + assert(checked!Hook1(c).toHash() != checked!Hook1(d)); + + // Hook with state, does not implement hookToHash + static struct Hook3 + { + ulong var1 = ulong.max; + uint var2 = uint.max; + } + + assert(checked!Hook3(12).toHash() != checked!Hook3(13).toHash()); + assert(checked!Hook3(13).toHash() == checked!Hook3(13).toHash()); + + // Hook with no state and no hookToHash, payload has its own hashing function + auto x1 = Checked!(Checked!int, ProperCompare)(123); + auto x2 = Checked!(Checked!int, ProperCompare)(123); + auto x3 = Checked!(Checked!int, ProperCompare)(144); + + assert(x1.toHash() == x2.toHash()); + assert(x1.toHash() != x3.toHash()); + assert(x2.toHash() != x3.toHash()); + + // Check shared. + { + shared shared0 = checked(12345678); + shared shared1 = checked!Hook1(123456789); + shared shared2 = checked!Hook2(234567891); + shared shared3 = checked!Hook3(345678912); + assert(shared0.toHash() == hashOf(shared0)); + assert(shared1.toHash() == hashOf(shared1)); + assert(shared2.toHash() == hashOf(shared2)); + assert(shared3.toHash() == hashOf(shared3)); + } +} + +/// +@safe unittest +{ + struct MyHook + { + static size_t hookToHash(T)(const T payload) nothrow @trusted + { + return .hashOf(payload); + } + } + + int[Checked!(int, MyHook)] aa; + Checked!(int, MyHook) var = 42; + aa[var] = 100; + + assert(aa[var] == 100); + + int[Checked!(int, Abort)] bb; + Checked!(int, Abort) var2 = 42; + bb[var2] = 100; + + assert(bb[var2] == 100); +} diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d index 756d1ca94bb..485b548b049 100644 --- a/libphobos/src/std/complex.d +++ b/libphobos/src/std/complex.d @@ -1015,6 +1015,14 @@ Complex!T tan(T)(Complex!T z) @safe pure nothrow @nogc @safe pure nothrow @nogc unittest { static import std.math; + + int ceqrel(T)(const Complex!T x, const Complex!T y) @safe pure nothrow @nogc + { + import std.math.operations : feqrel; + const r = feqrel(x.re, y.re); + const i = feqrel(x.im, y.im); + return r < i ? r : i; + } assert(ceqrel(tan(complex(1.0, 0.0)), complex(std.math.tan(1.0), 0.0)) >= double.mant_dig - 2); assert(ceqrel(tan(complex(0.0, 1.0)), complex(0.0, std.math.tanh(1.0))) >= double.mant_dig - 2); } @@ -1705,14 +1713,11 @@ Complex!T log10(T)(Complex!T x) @safe pure nothrow @nogc auto b = log10(complex(0.0, 1.0)) * 2.0; auto c = log10(complex(sqrt(2.0) / 2, sqrt(2.0) / 2)) * 4.0; assert(isClose(b, c, 0.0, 1e-15)); - - assert(ceqrel(log10(complex(-100.0L, 0.0L)), complex(2.0L, PI / LN10)) >= real.mant_dig - 1); - assert(ceqrel(log10(complex(-100.0L, -0.0L)), complex(2.0L, -PI / LN10)) >= real.mant_dig - 1); } @safe pure nothrow @nogc unittest { - import std.math.constants : PI; + import std.math.constants : LN10, PI; import std.math.operations : isClose; auto a = log10(fromPolar(1.0, PI / 6.0)); @@ -1732,6 +1737,9 @@ Complex!T log10(T)(Complex!T x) @safe pure nothrow @nogc auto f = log10(complex(-1.0L, 0.0L)); assert(isClose(f, complex(0.0L, 1.36437635384184134748L), 0.0, 1e-15)); + + assert(ceqrel(log10(complex(-100.0L, 0.0L)), complex(2.0L, PI / LN10)) >= real.mant_dig - 1); + assert(ceqrel(log10(complex(-100.0L, -0.0L)), complex(2.0L, -PI / LN10)) >= real.mant_dig - 1); } /** @@ -1771,9 +1779,6 @@ if (isIntegral!Int) assert(pow(a, 3) == a * a * a); assert(pow(a, -2) == 1.0 / (a * a)); assert(isClose(pow(a, -3), 1.0 / (a * a * a))); - - auto b = complex(2.0); - assert(ceqrel(pow(b, 3), exp(3 * log(b))) >= double.mant_dig - 1); } /// ditto @@ -1865,6 +1870,9 @@ Complex!T pow(T)(const T x, Complex!T n) @trusted pure nothrow @nogc auto d = pow(PI, complex(2.0, -1.0)); assert(ceqrel(d, complex(4.0790296880118296, -8.9872469554541869)) >= double.mant_dig - 1); + + auto e = complex(2.0); + assert(ceqrel(pow(e, 3), exp(3 * log(e))) >= double.mant_dig - 1); } @safe pure nothrow @nogc unittest diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 98df7fd1ccf..a10f4da7f9c 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -50,9 +50,9 @@ module std.conv; public import std.ascii : LetterCase; import std.meta; -import std.range.primitives; +import std.range; import std.traits; -import std.typecons : Flag, Yes, No, tuple; +import std.typecons : Flag, Yes, No, tuple, isTuple; // Same as std.string.format, but "self-importing". // Helps reduce code and imports, particularly in static asserts. @@ -653,6 +653,32 @@ if (isImplicitlyConvertible!(S, T) && }} } +// https://issues.dlang.org/show_bug.cgi?id=13551 +private T toImpl(T, S)(S value) +if (isTuple!T) +{ + T t; + static foreach (i; 0 .. T.length) + { + t[i] = value[i].to!(typeof(T[i])); + } + return t; +} + +@safe unittest +{ + import std.typecons : Tuple; + + auto test = ["10", "20", "30"]; + assert(test.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(10, 20, 30)); + + auto test1 = [1, 2]; + assert(test1.to!(Tuple!(int, int)) == Tuple!(int, int)(1, 2)); + + auto test2 = [1.0, 2.0, 3.0]; + assert(test2.to!(Tuple!(int, int, int)) == Tuple!(int, int, int)(1, 2, 3)); +} + /* Converting static arrays forwards to their dynamic counterparts. */ @@ -1970,7 +1996,7 @@ if (isInputRange!S && isSomeChar!(ElementEncodingType!S) && /// ditto private T toImpl(T, S)(S value, uint radix) -if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S) && +if (isSomeFiniteCharInputRange!S && isIntegral!T && is(typeof(parse!T(value, radix)))) { scope(success) diff --git a/libphobos/src/std/experimental/allocator/package.d b/libphobos/src/std/experimental/allocator/package.d index 2804829abe4..62f848f11fc 100644 --- a/libphobos/src/std/experimental/allocator/package.d +++ b/libphobos/src/std/experimental/allocator/package.d @@ -3735,7 +3735,13 @@ unittest Ternary r = (() @nogc => a.resolveInternalPointer(&b[0], p))(); assert(&p[0] == &b[0] && p.length >= b.length); r = a.resolveInternalPointer((() @trusted => &b[0] + b.length)(), p); - assert(&p[0] == &b[0] && p.length >= b.length); + + /* This line randomly fails on MacOS 12.x x64 + * https://issues.dlang.org/show_bug.cgi?id=22660 + * Commenting it out until someone can fix it. + */ + //assert(&p[0] == &b[0] && p.length >= b.length); + r = a.resolveInternalPointer((() @trusted => &b[0] + b.length / 2)(), p); assert(&p[0] == &b[0] && p.length >= b.length); auto bogus = new void[b.length]; diff --git a/libphobos/src/std/experimental/checkedint.d b/libphobos/src/std/experimental/checkedint.d index 354851bfc84..9237341d418 100644 --- a/libphobos/src/std/experimental/checkedint.d +++ b/libphobos/src/std/experimental/checkedint.d @@ -1,3467 +1,14 @@ -// Written in the D programming language. -/** -$(SCRIPT inhibitQuickIndex = 1;) - -This module defines facilities for efficient checking of integral operations -against overflow, casting with loss of precision, unexpected change of sign, -etc. The checking (and possibly correction) can be done at operation level, for -example $(LREF opChecked)$(D !"+"(x, y, overflow)) adds two integrals `x` and -`y` and sets `overflow` to `true` if an overflow occurred. The flag `overflow` -(a `bool` passed by reference) is not touched if the operation succeeded, so the -same flag can be reused for a sequence of operations and tested at the end. - -Issuing individual checked operations is flexible and efficient but often -tedious. The $(LREF Checked) facility offers encapsulated integral wrappers that -do all checking internally and have configurable behavior upon erroneous -results. For example, `Checked!int` is a type that behaves like `int` but aborts -execution immediately whenever involved in an operation that produces the -arithmetically wrong result. The accompanying convenience function $(LREF -checked) uses type deduction to convert a value `x` of integral type `T` to -`Checked!T` by means of `checked(x)`. For example: - ---- -void main() -{ - import std.experimental.checkedint, std.stdio; - writeln((checked(5) + 7).get); // 12 - writeln((checked(10) * 1000 * 1000 * 1000).get); // Overflow -} ---- - -Similarly, $(D checked(-1) > uint(0)) aborts execution (even though the built-in -comparison $(D int(-1) > uint(0)) is surprisingly true due to language's -conversion rules modeled after C). Thus, `Checked!int` is a virtually drop-in -replacement for `int` useable in debug builds, to be replaced by `int` in -release mode if efficiency demands it. - -`Checked` has customizable behavior with the help of a second type parameter, -`Hook`. Depending on what methods `Hook` defines, core operations on the -underlying integral may be verified for overflow or completely redefined. If -`Hook` defines no method at all and carries no state, there is no change in -behavior, i.e. $(D Checked!(int, void)) is a wrapper around `int` that adds no -customization at all. - -This module provides a few predefined hooks (below) that add useful behavior to -`Checked`: - -$(BOOKTABLE , - $(TR $(TD $(LREF Abort)) $(TD - fails every incorrect operation with a message to $(REF - stderr, std, stdio) followed by a call to `assert(0)`. It is the default - second parameter, i.e. `Checked!short` is the same as - $(D Checked!(short, Abort)). - )) - $(TR $(TD $(LREF Throw)) $(TD - fails every incorrect operation by throwing an exception. - )) - $(TR $(TD $(LREF Warn)) $(TD - prints incorrect operations to $(REF stderr, std, stdio) - but otherwise preserves the built-in behavior. - )) - $(TR $(TD $(LREF ProperCompare)) $(TD - fixes the comparison operators `==`, `!=`, `<`, `<=`, `>`, and `>=` - to return correct results in all circumstances, - at a slight cost in efficiency. For example, - $(D Checked!(uint, ProperCompare)(1) > -1) is `true`, - which is not the case for the built-in comparison. Also, comparing - numbers for equality with floating-point numbers only passes if the - integral can be converted to the floating-point number precisely, - so as to preserve transitivity of equality. - )) - $(TR $(TD $(LREF WithNaN)) $(TD - reserves a special "Not a Number" (NaN) value akin to the homonym value - reserved for floating-point values. Once a $(D Checked!(X, WithNaN)) - gets this special value, it preserves and propagates it until - reassigned. $(LREF isNaN) can be used to query whether the object - is not a number. - )) - $(TR $(TD $(LREF Saturate)) $(TD - implements saturating arithmetic, i.e. $(D Checked!(int, Saturate)) - "stops" at `int.max` for all operations that would cause an `int` to - overflow toward infinity, and at `int.min` for all operations that would - correspondingly overflow toward negative infinity. - )) -) - - -These policies may be used alone, e.g. $(D Checked!(uint, WithNaN)) defines a -`uint`-like type that reaches a stable NaN state for all erroneous operations. -They may also be "stacked" on top of each other, owing to the property that a -checked integral emulates an actual integral, which means another checked -integral can be built on top of it. Some combinations of interest include: - -$(BOOKTABLE , - $(TR $(TD $(D Checked!(Checked!int, ProperCompare)))) - $(TR $(TD -defines an `int` with fixed -comparison operators that will fail with `assert(0)` upon overflow. (Recall that -`Abort` is the default policy.) The order in which policies are combined is -important because the outermost policy (`ProperCompare` in this case) has the -first crack at intercepting an operator. The converse combination $(D -Checked!(Checked!(int, ProperCompare))) is meaningless because `Abort` will -intercept comparison and will fail without giving `ProperCompare` a chance to -intervene. - )) - $(TR $(TD)) - $(TR $(TDNW $(D Checked!(Checked!(int, ProperCompare), WithNaN)))) - $(TR $(TD -defines an `int`-like -type that supports a NaN value. For values that are not NaN, comparison works -properly. Again the composition order is important; $(D Checked!(Checked!(int, -WithNaN), ProperCompare)) does not have good semantics because `ProperCompare` -intercepts comparisons before the numbers involved are tested for NaN. - )) -) - -The hook's members are looked up statically in a Design by Introspection manner -and are all optional. The table below illustrates the members that a hook type -may define and their influence over the behavior of the `Checked` type using it. -In the table, `hook` is an alias for `Hook` if the type `Hook` does not -introduce any state, or an object of type `Hook` otherwise. - -$(TABLE , -$(TR $(TH `Hook` member) $(TH Semantics in $(D Checked!(T, Hook))) -) -$(TR $(TD `defaultValue`) $(TD If defined, `Hook.defaultValue!T` is used as the -default initializer of the payload.) -) -$(TR $(TD `min`) $(TD If defined, `Hook.min!T` is used as the minimum value of -the payload.) -) -$(TR $(TD `max`) $(TD If defined, `Hook.max!T` is used as the maximum value of -the payload.) -) -$(TR $(TD `hookOpCast`) $(TD If defined, `hook.hookOpCast!U(get)` is forwarded -to unconditionally when the payload is to be cast to type `U`.) -) -$(TR $(TD `onBadCast`) $(TD If defined and `hookOpCast` is $(I not) defined, -`onBadCast!U(get)` is forwarded to when the payload is to be cast to type `U` -and the cast would lose information or force a change of sign.) -) -$(TR $(TD `hookOpEquals`) $(TD If defined, $(D hook.hookOpEquals(get, rhs)) is -forwarded to unconditionally when the payload is compared for equality against -value `rhs` of integral, floating point, or Boolean type.) -) -$(TR $(TD `hookOpCmp`) $(TD If defined, $(D hook.hookOpCmp(get, rhs)) is -forwarded to unconditionally when the payload is compared for ordering against -value `rhs` of integral, floating point, or Boolean type.) -) -$(TR $(TD `hookOpUnary`) $(TD If defined, `hook.hookOpUnary!op(get)` (where `op` -is the operator symbol) is forwarded to for unary operators `-` and `~`. In -addition, for unary operators `++` and `--`, `hook.hookOpUnary!op(payload)` is -called, where `payload` is a reference to the value wrapped by `Checked` so the -hook can change it.) -) -$(TR $(TD `hookOpBinary`) $(TD If defined, $(D hook.hookOpBinary!op(get, rhs)) -(where `op` is the operator symbol and `rhs` is the right-hand side operand) is -forwarded to unconditionally for binary operators `+`, `-`, `*`, `/`, `%`, -`^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) -) -$(TR $(TD `hookOpBinaryRight`) $(TD If defined, $(D -hook.hookOpBinaryRight!op(lhs, get)) (where `op` is the operator symbol and -`lhs` is the left-hand side operand) is forwarded to unconditionally for binary -operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.) -) -$(TR $(TD `onOverflow`) $(TD If defined, `hook.onOverflow!op(get)` is forwarded -to for unary operators that overflow but only if `hookOpUnary` is not defined. -Unary `~` does not overflow; unary `-` overflows only when the most negative -value of a signed type is negated, and the result of the hook call is returned. -When the increment or decrement operators overflow, the payload is assigned the -result of `hook.onOverflow!op(get)`. When a binary operator overflows, the -result of $(D hook.onOverflow!op(get, rhs)) is returned, but only if `Hook` does -not define `hookOpBinary`.) -) -$(TR $(TD `hookOpOpAssign`) $(TD If defined, $(D hook.hookOpOpAssign!op(payload, -rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side -operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, -`^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.) -) -$(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound)) -(where `value` is the value being assigned) is forwarded to when the result of -binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, -and `>>>=` is smaller than the smallest value representable by `T`.) -) -$(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound)) -(where `value` is the value being assigned) is forwarded to when the result of -binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, -and `>>>=` is larger than the largest value representable by `T`.) -) -$(TR $(TD `hookToHash`) $(TD If defined, $(D hook.hookToHash(payload)) -(where `payload` is a reference to the value wrapped by Checked) is forwarded -to when `toHash` is called on a Checked type. Custom hashing can be implemented -in a `Hook`, otherwise the built-in hashing is used.) -) -) - -Source: $(PHOBOSSRC std/experimental/checkedint.d) -*/ -module std.experimental.checkedint; -import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual; - -/// -@safe unittest -{ - int[] concatAndAdd(int[] a, int[] b, int offset) - { - // Aborts on overflow on size computation - auto r = new int[(checked(a.length) + b.length).get]; - // Aborts on overflow on element computation - foreach (i; 0 .. a.length) - r[i] = (a[i] + checked(offset)).get; - foreach (i; 0 .. b.length) - r[i + a.length] = (b[i] + checked(offset)).get; - return r; - } - assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]); -} - - -/// `Saturate` stops at an overflow -@safe unittest -{ - auto x = (cast(byte) 127).checked!Saturate; - assert(x == 127); - x++; - assert(x == 127); -} - -/// `WithNaN` has a special "Not a Number" (NaN) value akin to the homonym value reserved for floating-point values -@safe unittest -{ - auto x = 100.checked!WithNaN; - assert(x == 100); - x /= 0; - assert(x.isNaN); -} - -/// `ProperCompare` fixes the comparison operators ==, !=, <, <=, >, and >= to return correct results -@safe unittest -{ - uint x = 1; - auto y = x.checked!ProperCompare; - assert(x < -1); // built-in comparison - assert(y > -1); // ProperCompare -} - -/// `Throw` fails every incorrect operation by throwing an exception -@safe unittest -{ - import std.exception : assertThrown; - auto x = -1.checked!Throw; - assertThrown(x / 0); - assertThrown(x + int.min); - assertThrown(x == uint.max); -} - -/** -Checked integral type wraps an integral `T` and customizes its behavior with the -help of a `Hook` type. The type wrapped must be one of the predefined integrals -(unqualified), or another instance of `Checked`. -*/ -struct Checked(T, Hook = Abort) -if (isIntegral!T || is(T == Checked!(U, H), U, H)) -{ - import std.algorithm.comparison : among; - import std.experimental.allocator.common : stateSize; - import std.format.spec : FormatSpec; - import std.range.primitives : isInputRange, ElementType; - import std.traits : hasMember, isSomeChar; - - /** - The type of the integral subject to checking. - */ - alias Representation = T; - - // state { - static if (hasMember!(Hook, "defaultValue")) - private T payload = Hook.defaultValue!T; - else - private T payload; - /** - `hook` is a member variable if it has state, or an alias for `Hook` - otherwise. - */ - static if (stateSize!Hook > 0) Hook hook; - else alias hook = Hook; - // } state - - // get - /** - Returns a copy of the underlying value. - */ - auto get() inout { return payload; } - /// - @safe unittest - { - auto x = checked(ubyte(42)); - static assert(is(typeof(x.get()) == ubyte)); - assert(x.get == 42); - const y = checked(ubyte(42)); - static assert(is(typeof(y.get()) == const ubyte)); - assert(y.get == 42); - } - - /** - Defines the minimum and maximum. These values are hookable by defining - `Hook.min` and/or `Hook.max`. - */ - static if (hasMember!(Hook, "min")) - { - enum Checked!(T, Hook) min = Checked!(T, Hook)(Hook.min!T); - /// - @safe unittest - { - assert(Checked!short.min == -32768); - assert(Checked!(short, WithNaN).min == -32767); - assert(Checked!(uint, WithNaN).max == uint.max - 1); - } - } - else - enum Checked!(T, Hook) min = Checked(T.min); - /// ditto - static if (hasMember!(Hook, "max")) - enum Checked!(T, Hook) max = Checked(Hook.max!T); - else - enum Checked!(T, Hook) max = Checked(T.max); - - /** - Constructor taking a value properly convertible to the underlying type. `U` - may be either an integral that can be converted to `T` without a loss, or - another `Checked` instance whose representation may be in turn converted to - `T` without a loss. - */ - this(U)(U rhs) - if (valueConvertible!(U, T) || - !isIntegral!T && is(typeof(T(rhs))) || - is(U == Checked!(V, W), V, W) && - is(typeof(Checked!(T, Hook)(rhs.get)))) - { - static if (isIntegral!U) - payload = rhs; - else - payload = rhs.payload; - } - /// - @safe unittest - { - auto a = checked(42L); - assert(a == 42); - auto b = Checked!long(4242); // convert 4242 to long - assert(b == 4242); - } - - /** - Assignment operator. Has the same constraints as the constructor. - */ - ref Checked opAssign(U)(U rhs) return - if (is(typeof(Checked!(T, Hook)(rhs)))) - { - static if (isIntegral!U) - payload = rhs; - else - payload = rhs.payload; - return this; - } - /// - @safe unittest - { - Checked!long a; - a = 42L; - assert(a == 42); - a = 4242; - assert(a == 4242); - } - - /// - @safe unittest - { - Checked!long a, b; - a = b = 3; - assert(a == 3 && b == 3); - } - - /** - Construct from a decimal string. The conversion follows the same rules as - $(REF to, std, conv) converting a string to the wrapped `T` type. - - Params: - str = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - of characters - */ - this(Range)(Range str) - if (isInputRange!Range && isSomeChar!(ElementType!Range)) - { - import std.conv : to; - - this(to!T(str)); - } - - /** - $(REF to, std, conv) can convert a string to a `Checked!T`: - */ - @system unittest - { - import std.conv : to; - - const a = to!long("1234"); - const b = to!(Checked!long)("1234"); - assert(a == b); - } - - // opCast - /** - Casting operator to integral, `bool`, or floating point type. If `Hook` - defines `hookOpCast`, the call immediately returns - `hook.hookOpCast!U(get)`. Otherwise, casting to `bool` yields $(D - get != 0) and casting to another integral that can represent all - values of `T` returns `get` promoted to `U`. - - If a cast to a floating-point type is requested and `Hook` defines - `onBadCast`, the cast is verified by ensuring $(D get == cast(T) - U(get)). If that is not `true`, `hook.onBadCast!U(get)` is returned. - - If a cast to an integral type is requested and `Hook` defines `onBadCast`, - the cast is verified by ensuring `get` and $(D cast(U) - get) are the same arithmetic number. (Note that `int(-1)` and - `uint(1)` are different values arithmetically although they have the same - bitwise representation and compare equal by language rules.) If the numbers - are not arithmetically equal, `hook.onBadCast!U(get)` is - returned. - - */ - U opCast(U, this _)() - if (isIntegral!U || isFloatingPoint!U || is(U == bool)) - { - static if (hasMember!(Hook, "hookOpCast")) - { - return hook.hookOpCast!U(payload); - } - else static if (is(U == bool)) - { - return payload != 0; - } - else static if (valueConvertible!(T, U)) - { - return payload; - } - // may lose bits or precision - else static if (!hasMember!(Hook, "onBadCast")) - { - return cast(U) payload; - } - else - { - if (isUnsigned!T || !isUnsigned!U || - T.sizeof > U.sizeof || payload >= 0) - { - auto result = cast(U) payload; - // If signedness is different, we need additional checks - if (result == payload && - (!isUnsigned!T || isUnsigned!U || result >= 0)) - return result; - } - return hook.onBadCast!U(payload); - } - } - /// - @safe unittest - { - assert(cast(uint) checked(42) == 42); - assert(cast(uint) checked!WithNaN(-42) == uint.max); - } - - // opEquals - /** - Compares `this` against `rhs` for equality. If `Hook` defines - `hookOpEquals`, the function forwards to $(D - hook.hookOpEquals(get, rhs)). Otherwise, the result of the - built-in operation $(D get == rhs) is returned. - - If `U` is also an instance of `Checked`, both hooks (left- and right-hand - side) are introspected for the method `hookOpEquals`. If both define it, - priority is given to the left-hand side. - - */ - bool opEquals(U, this _)(U rhs) - if (isIntegral!U || isFloatingPoint!U || is(U == bool) || - is(U == Checked!(V, W), V, W) && is(typeof(this == rhs.payload))) - { - static if (is(U == Checked!(V, W), V, W)) - { - alias R = typeof(payload + rhs.payload); - static if (is(Hook == W)) - { - // Use the lhs hook if there - return this == rhs.payload; - } - else static if (valueConvertible!(T, R) && valueConvertible!(V, R)) - { - return payload == rhs.payload; - } - else static if (hasMember!(Hook, "hookOpEquals")) - { - return hook.hookOpEquals(payload, rhs.payload); - } - else static if (hasMember!(W, "hookOpEquals")) - { - return rhs.hook.hookOpEquals(rhs.payload, payload); - } - else - { - return payload == rhs.payload; - } - } - else static if (hasMember!(Hook, "hookOpEquals")) - return hook.hookOpEquals(payload, rhs); - else static if (isIntegral!U || isFloatingPoint!U || is(U == bool)) - return payload == rhs; - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - import std.traits : isUnsigned; - - static struct MyHook - { - static bool thereWereErrors; - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - if (lhs != rhs) return false; - static if (isUnsigned!L && !isUnsigned!R) - { - if (lhs > 0 && rhs < 0) thereWereErrors = true; - } - else static if (isUnsigned!R && !isUnsigned!L) - if (lhs < 0 && rhs > 0) thereWereErrors = true; - // Preserve built-in behavior. - return true; - } - } - auto a = checked!MyHook(-42); - assert(a == uint(-42)); - assert(MyHook.thereWereErrors); - MyHook.thereWereErrors = false; - assert(checked!MyHook(uint(-42)) == -42); - assert(MyHook.thereWereErrors); - static struct MyHook2 - { - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - return lhs == rhs; - } - } - MyHook.thereWereErrors = false; - assert(checked!MyHook2(uint(-42)) == a); - // Hook on left hand side takes precedence, so no errors - assert(!MyHook.thereWereErrors); - } - - // toHash - /** - Generates a hash for `this`. If `Hook` defines `hookToHash`, the call - immediately returns `hook.hookToHash(payload)`. If `Hook` does not - implement `hookToHash`, but it has state, a hash will be generated for - the `Hook` using the built-in function and it will be xored with the - hash of the `payload`. - */ - size_t toHash() const nothrow @safe - { - static if (hasMember!(Hook, "hookToHash")) - { - return hook.hookToHash(payload); - } - else static if (stateSize!Hook > 0) - { - static if (hasMember!(typeof(payload), "toHash")) - { - return payload.toHash() ^ hashOf(hook); - } - else - { - return hashOf(payload) ^ hashOf(hook); - } - } - else static if (hasMember!(typeof(payload), "toHash")) - { - return payload.toHash(); - } - else - { - return .hashOf(payload); - } - } - - /// ditto - size_t toHash(this _)() shared const nothrow @safe - { - import core.atomic : atomicLoad, MemoryOrder; - static if (is(typeof(this.payload.atomicLoad!(MemoryOrder.acq)) P)) - { - auto payload = __ctfe ? cast(P) this.payload - : this.payload.atomicLoad!(MemoryOrder.acq); - } - else - { - alias payload = this.payload; - } - - static if (hasMember!(Hook, "hookToHash")) - { - return hook.hookToHash(payload); - } - else static if (stateSize!Hook > 0) - { - static if (hasMember!(typeof(payload), "toHash")) - { - return payload.toHash() ^ hashOf(hook); - } - else - { - return hashOf(payload) ^ hashOf(hook); - } - } - else static if (hasMember!(typeof(payload), "toHash")) - { - return payload.toHash(); - } - else - { - return .hashOf(payload); - } - } - - /** - Writes a string representation of this to a `sink`. - - Params: - sink = A `Char` accepting - $(REF_ALTTEXT output range, isOutputRange, std,range,primitives). - fmt = A $(REF FormatSpec, std, format) which controls how this - is formatted. - */ - void toString(Writer, Char)(scope ref Writer sink, scope const ref FormatSpec!Char fmt) const - { - import std.format.write : formatValue; - if (fmt.spec == 's') - return formatValue(sink, this, fmt); - else - return formatValue(sink, payload, fmt); - } - - /** - `toString` is rarely directly invoked; the usual way of using it is via - $(REF format, std, format): - */ - @system unittest - { - import std.format; - - assert(format("%04d", checked(15)) == "0015"); - assert(format("0x%02x", checked(15)) == "0x0f"); - } - - // opCmp - /** - - Compares `this` against `rhs` for ordering. If `Hook` defines `hookOpCmp`, - the function forwards to $(D hook.hookOpCmp(get, rhs)). Otherwise, the - result of the built-in comparison operation is returned. - - If `U` is also an instance of `Checked`, both hooks (left- and right-hand - side) are introspected for the method `hookOpCmp`. If both define it, - priority is given to the left-hand side. - - */ - auto opCmp(U, this _)(const U rhs) //const pure @safe nothrow @nogc - if (isIntegral!U || isFloatingPoint!U || is(U == bool)) - { - static if (hasMember!(Hook, "hookOpCmp")) - { - return hook.hookOpCmp(payload, rhs); - } - else static if (valueConvertible!(T, U) || valueConvertible!(U, T)) - { - return payload < rhs ? -1 : payload > rhs; - } - else static if (isFloatingPoint!U) - { - U lhs = payload; - return lhs < rhs ? U(-1.0) - : lhs > rhs ? U(1.0) - : lhs == rhs ? U(0.0) : U.init; - } - else - { - return payload < rhs ? -1 : payload > rhs; - } - } - - /// ditto - auto opCmp(U, Hook1, this _)(Checked!(U, Hook1) rhs) - { - alias R = typeof(payload + rhs.payload); - static if (valueConvertible!(T, R) && valueConvertible!(U, R)) - { - return payload < rhs.payload ? -1 : payload > rhs.payload; - } - else static if (is(Hook == Hook1)) - { - // Use the lhs hook - return this.opCmp(rhs.payload); - } - else static if (hasMember!(Hook, "hookOpCmp")) - { - return hook.hookOpCmp(get, rhs.get); - } - else static if (hasMember!(Hook1, "hookOpCmp")) - { - return -rhs.hook.hookOpCmp(rhs.payload, get); - } - else - { - return payload < rhs.payload ? -1 : payload > rhs.payload; - } - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - import std.traits : isUnsigned; - - static struct MyHook - { - static bool thereWereErrors; - static int hookOpCmp(L, R)(L lhs, R rhs) - { - static if (isUnsigned!L && !isUnsigned!R) - { - if (rhs < 0 && rhs >= lhs) - thereWereErrors = true; - } - else static if (isUnsigned!R && !isUnsigned!L) - { - if (lhs < 0 && lhs >= rhs) - thereWereErrors = true; - } - // Preserve built-in behavior. - return lhs < rhs ? -1 : lhs > rhs; - } - } - auto a = checked!MyHook(-42); - assert(a > uint(42)); - assert(MyHook.thereWereErrors); - static struct MyHook2 - { - static int hookOpCmp(L, R)(L lhs, R rhs) - { - // Default behavior - return lhs < rhs ? -1 : lhs > rhs; - } - } - MyHook.thereWereErrors = false; - assert(Checked!(uint, MyHook2)(uint(-42)) <= a); - //assert(Checked!(uint, MyHook2)(uint(-42)) >= a); - // Hook on left hand side takes precedence, so no errors - assert(!MyHook.thereWereErrors); - assert(a <= Checked!(uint, MyHook2)(uint(-42))); - assert(MyHook.thereWereErrors); - } - - // For coverage - static if (is(T == int) && is(Hook == void)) @safe unittest - { - assert(checked(42) <= checked!void(42)); - assert(checked!void(42) <= checked(42u)); - assert(checked!void(42) <= checked!(void*)(42u)); - } - - // opUnary - /** - - Defines unary operators `+`, `-`, `~`, `++`, and `--`. Unary `+` is not - overridable and always has built-in behavior (returns `this`). For the - others, if `Hook` defines `hookOpUnary`, `opUnary` forwards to $(D - Checked!(typeof(hook.hookOpUnary!op(get)), - Hook)(hook.hookOpUnary!op(get))). - - If `Hook` does not define `hookOpUnary` but defines `onOverflow`, `opUnary` - forwards to `hook.onOverflow!op(get)` in case an overflow occurs. - For `++` and `--`, the payload is assigned from the result of the call to - `onOverflow`. - - Note that unary `-` is considered to overflow if `T` is a signed integral of - 32 or 64 bits and is equal to the most negative value. This is because that - value has no positive negation. - - */ - auto opUnary(string op, this _)() - if (op == "+" || op == "-" || op == "~") - { - static if (op == "+") - return Checked(this); // "+" is not hookable - else static if (hasMember!(Hook, "hookOpUnary")) - { - auto r = hook.hookOpUnary!op(payload); - return Checked!(typeof(r), Hook)(r); - } - else static if (op == "-" && isIntegral!T && T.sizeof >= 4 && - !isUnsigned!T && hasMember!(Hook, "onOverflow")) - { - static assert(is(typeof(-payload) == typeof(payload))); - bool overflow; - import core.checkedint : negs; - auto r = negs(payload, overflow); - if (overflow) r = hook.onOverflow!op(payload); - return Checked(r); - } - else - return Checked(mixin(op ~ "payload")); - } - - /// ditto - ref Checked opUnary(string op)() return - if (op == "++" || op == "--") - { - static if (hasMember!(Hook, "hookOpUnary")) - hook.hookOpUnary!op(payload); - else static if (hasMember!(Hook, "onOverflow")) - { - static if (op == "++") - { - if (payload == max.payload) - payload = hook.onOverflow!"++"(payload); - else - ++payload; - } - else - { - if (payload == min.payload) - payload = hook.onOverflow!"--"(payload); - else - --payload; - } - } - else - mixin(op ~ "payload;"); - return this; - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - static struct MyHook - { - static bool thereWereErrors; - static L hookOpUnary(string x, L)(L lhs) - { - if (x == "-" && lhs == -lhs) thereWereErrors = true; - return -lhs; - } - } - auto a = checked!MyHook(long.min); - assert(a == -a); - assert(MyHook.thereWereErrors); - auto b = checked!void(42); - assert(++b == 43); - } - - // opBinary - /** - - Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, - and `>>>`. If `Hook` defines `hookOpBinary`, `opBinary` forwards to $(D - Checked!(typeof(hook.hookOpBinary!op(get, rhs)), - Hook)(hook.hookOpBinary!op(get, rhs))). - - If `Hook` does not define `hookOpBinary` but defines `onOverflow`, - `opBinary` forwards to `hook.onOverflow!op(get, rhs)` in case an - overflow occurs. - - If two `Checked` instances are involved in a binary operation and both - define `hookOpBinary`, the left-hand side hook has priority. If both define - `onOverflow`, a compile-time error occurs. - - */ - auto opBinary(string op, Rhs)(const Rhs rhs) - if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) - { - return opBinaryImpl!(op, Rhs, typeof(this))(rhs); - } - - /// ditto - auto opBinary(string op, Rhs)(const Rhs rhs) const - if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) - { - return opBinaryImpl!(op, Rhs, typeof(this))(rhs); - } - - private auto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs) - { - alias R = typeof(mixin("payload" ~ op ~ "rhs")); - static assert(is(typeof(mixin("payload" ~ op ~ "rhs")) == R)); - static if (isIntegral!R) alias Result = Checked!(R, Hook); - else alias Result = R; - - static if (hasMember!(Hook, "hookOpBinary")) - { - auto r = hook.hookOpBinary!op(payload, rhs); - return Checked!(typeof(r), Hook)(r); - } - else static if (is(Rhs == bool)) - { - return mixin("this" ~ op ~ "ubyte(rhs)"); - } - else static if (isFloatingPoint!Rhs) - { - return mixin("payload" ~ op ~ "rhs"); - } - else static if (hasMember!(Hook, "onOverflow")) - { - bool overflow; - auto r = opChecked!op(payload, rhs, overflow); - if (overflow) r = hook.onOverflow!op(payload, rhs); - return Result(r); - } - else - { - // Default is built-in behavior - return Result(mixin("payload" ~ op ~ "rhs")); - } - } - - /// ditto - auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) - { - return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); - } - - /// ditto - auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) const - { - return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs); - } - - private - auto opBinaryImpl2(string op, U, Hook1, this _)(Checked!(U, Hook1) rhs) - { - alias R = typeof(get + rhs.payload); - static if (valueConvertible!(T, R) && valueConvertible!(U, R) || - is(Hook == Hook1)) - { - // Delegate to lhs - return mixin("this" ~ op ~ "rhs.payload"); - } - else static if (hasMember!(Hook, "hookOpBinary")) - { - return hook.hookOpBinary!op(payload, rhs); - } - else static if (hasMember!(Hook1, "hookOpBinary")) - { - // Delegate to rhs - return mixin("this.payload" ~ op ~ "rhs"); - } - else static if (hasMember!(Hook, "onOverflow") && - !hasMember!(Hook1, "onOverflow")) - { - // Delegate to lhs - return mixin("this" ~ op ~ "rhs.payload"); - } - else static if (hasMember!(Hook1, "onOverflow") && - !hasMember!(Hook, "onOverflow")) - { - // Delegate to rhs - return mixin("this.payload" ~ op ~ "rhs"); - } - else - { - static assert(0, "Conflict between lhs and rhs hooks," ~ - " use .get on one side to disambiguate."); - } - } - - static if (is(T == int) && is(Hook == void)) @safe unittest - { - const a = checked(42); - assert(a + 1 == 43); - assert(a + checked(uint(42)) == 84); - assert(checked(42) + checked!void(42u) == 84); - assert(checked!void(42) + checked(42u) == 84); - - static struct MyHook - { - static uint tally; - static auto hookOpBinary(string x, L, R)(L lhs, R rhs) - { - ++tally; - return mixin("lhs" ~ x ~ "rhs"); - } - } - assert(checked!MyHook(42) + checked(42u) == 84); - assert(checked!void(42) + checked!MyHook(42u) == 84); - assert(MyHook.tally == 2); - } - - // opBinaryRight - /** - - Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, - `>>`, and `>>>` for the case when a built-in numeric or Boolean type is on - the left-hand side, and a `Checked` instance is on the right-hand side. - - */ - auto opBinaryRight(string op, Lhs)(const Lhs lhs) - if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) - { - return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); - } - - /// ditto - auto opBinaryRight(string op, Lhs)(const Lhs lhs) const - if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool)) - { - return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs); - } - - private auto opBinaryRightImpl(string op, Lhs, this _)(const Lhs lhs) - { - static if (hasMember!(Hook, "hookOpBinaryRight")) - { - auto r = hook.hookOpBinaryRight!op(lhs, payload); - return Checked!(typeof(r), Hook)(r); - } - else static if (hasMember!(Hook, "hookOpBinary")) - { - auto r = hook.hookOpBinary!op(lhs, payload); - return Checked!(typeof(r), Hook)(r); - } - else static if (is(Lhs == bool)) - { - return mixin("ubyte(lhs)" ~ op ~ "this"); - } - else static if (isFloatingPoint!Lhs) - { - return mixin("lhs" ~ op ~ "payload"); - } - else static if (hasMember!(Hook, "onOverflow")) - { - bool overflow; - auto r = opChecked!op(lhs, T(payload), overflow); - if (overflow) r = hook.onOverflow!op(lhs, payload); - return Checked!(typeof(r), Hook)(r); - } - else - { - // Default is built-in behavior - auto r = mixin("lhs" ~ op ~ "T(payload)"); - return Checked!(typeof(r), Hook)(r); - } - } - - static if (is(T == int) && is(Hook == void)) @safe unittest - { - assert(1 + checked(1) == 2); - static uint tally; - static struct MyHook - { - static auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) - { - ++tally; - return mixin("lhs" ~ x ~ "rhs"); - } - } - assert(1 + checked!MyHook(1) == 2); - assert(tally == 1); - - immutable x1 = checked(1); - assert(1 + x1 == 2); - immutable x2 = checked!MyHook(1); - assert(1 + x2 == 2); - assert(tally == 2); - } - - // opOpAssign - /** - - Defines operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, - `<<=`, `>>=`, and `>>>=`. - - If `Hook` defines `hookOpOpAssign`, `opOpAssign` forwards to - `hook.hookOpOpAssign!op(payload, rhs)`, where `payload` is a reference to - the internally held data so the hook can change it. - - Otherwise, the operator first evaluates $(D auto result = - opBinary!op(payload, rhs).payload), which is subject to the hooks in - `opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if - `Hook` defines `onLowerBound`, the payload is assigned from $(D - hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T, - Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned - from $(D hook.onUpperBound(result, min)). - - If the right-hand side is also a Checked but with a different hook or - underlying type, the hook and underlying type of this Checked takes - precedence. - - In all other cases, the built-in behavior is carried out. - - Params: - op = The operator involved (without the `"="`, e.g. `"+"` for `"+="` etc) - rhs = The right-hand side of the operator (left-hand side is `this`) - - Returns: A reference to `this`. - */ - ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return - if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool)) - { - static assert(is(typeof(mixin("payload" ~ op ~ "=rhs")) == T)); - - static if (hasMember!(Hook, "hookOpOpAssign")) - { - hook.hookOpOpAssign!op(payload, rhs); - } - else - { - alias R = typeof(get + rhs); - auto r = opBinary!op(rhs).get; - import std.conv : unsigned; - - static if (ProperCompare.hookOpCmp(R.min, min.get) < 0 && - hasMember!(Hook, "onLowerBound")) - { - if (ProperCompare.hookOpCmp(r, min.get) < 0) - { - // Example: Checked!uint(1) += int(-3) - payload = hook.onLowerBound(r, min.get); - return this; - } - } - static if (ProperCompare.hookOpCmp(max.get, R.max) < 0 && - hasMember!(Hook, "onUpperBound")) - { - if (ProperCompare.hookOpCmp(r, max.get) > 0) - { - // Example: Checked!uint(1) += long(uint.max) - payload = hook.onUpperBound(r, max.get); - return this; - } - } - payload = cast(T) r; - } - return this; - } - - /// ditto - ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return - if (is(Rhs == Checked!(RhsT, RhsHook), RhsT, RhsHook)) - { - return opOpAssign!(op, typeof(rhs.payload))(rhs.payload); - } - - /// - static if (is(T == int) && is(Hook == void)) @safe unittest - { - static struct MyHook - { - static bool thereWereErrors; - static T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - thereWereErrors = true; - return bound; - } - static T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - thereWereErrors = true; - return bound; - } - } - auto x = checked!MyHook(byte.min); - x -= 1; - assert(MyHook.thereWereErrors); - MyHook.thereWereErrors = false; - x = byte.max; - x += 1; - assert(MyHook.thereWereErrors); - } -} - -/** - -Convenience function that turns an integral into the corresponding `Checked` -instance by using template argument deduction. The hook type may be specified -(by default `Abort`). - -*/ -Checked!(T, Hook) checked(Hook = Abort, T)(const T value) -if (is(typeof(Checked!(T, Hook)(value)))) -{ - return Checked!(T, Hook)(value); -} - -/// -@safe unittest -{ - static assert(is(typeof(checked(42)) == Checked!int)); - assert(checked(42) == Checked!int(42)); - static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN))); - assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42)); -} - -// get -@safe unittest -{ - void test(T)() - { - assert(Checked!(T, void)(ubyte(22)).get == 22); - } - test!ubyte; - test!(const ubyte); - test!(immutable ubyte); -} - -@system unittest -{ - // https://issues.dlang.org/show_bug.cgi?id=21758 - assert(4 * checked(5L) == 20); - assert(20 / checked(5L) == 4); - assert(2 ^^ checked(3L) == 8); - assert(12 % checked(5L) == 2); - assert((0xff & checked(3L)) == 3); - assert((0xf0 | checked(3L)) == 0xf3); - assert((0xff ^ checked(3L)) == 0xfc); -} - -// Abort -/** - -Force all integral errors to fail by printing an error message to `stderr` and -then abort the program. `Abort` is the default second argument for `Checked`. - -*/ -struct Abort -{ -static: - /** - - Called automatically upon a bad cast (one that loses precision or attempts - to convert a negative value to an unsigned type). The source type is `Src` - and the destination type is `Dst`. - - Params: - src = The source of the cast - - Returns: Nominally the result is the desired value of the cast operation, - which will be forwarded as the result of the cast. For `Abort`, the - function never returns because it aborts the program. - - */ - Dst onBadCast(Dst, Src)(Src src) - { - Warn.onBadCast!Dst(src); - assert(0); - } - - /** - - Called automatically upon a bounds error. - - Params: - rhs = The right-hand side value in the assignment, after the operator has - been evaluated - bound = The value of the bound being violated - - Returns: Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Abort`, the function never returns because - it aborts the program. - - */ - T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - Warn.onLowerBound(rhs, bound); - assert(0); - } - /// ditto - T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - Warn.onUpperBound(rhs, bound); - assert(0); - } - - /** - - Called automatically upon a comparison for equality. In case of a erroneous - comparison (one that would make a signed negative value appear equal to an - unsigned positive value), this hook issues `assert(0)` which terminates the - application. - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: Upon a correct comparison, returns the result of the comparison. - Otherwise, the function terminates the application so it never returns. - - */ - static bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - Warn.hookOpEquals(lhs, rhs); - assert(0); - } - return result; - } - - /** - - Called automatically upon a comparison for ordering using one of the - operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. - it would make a signed negative value appear greater than or equal to an - unsigned positive value), then application is terminated with `assert(0)`. - Otherwise, the three-state result is returned (positive if $(D lhs > rhs), - negative if $(D lhs < rhs), `0` otherwise). - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: For correct comparisons, returns a positive integer if $(D lhs > - rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. Upon - a mistaken comparison such as $(D int(-1) < uint(0)), the function never - returns because it aborts the program. - - */ - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"cmp"(lhs, rhs, error); - if (error) - { - Warn.hookOpCmp(lhs, rhs); - assert(0); - } - return result; - } - - /** - - Called automatically upon an overflow during a unary or binary operation. - - Params: - x = The operator, e.g. `-` - lhs = The left-hand side (or sole) argument - rhs = The right-hand side type involved in the operator - - Returns: Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Abort`, the function never returns because - it aborts the program. - - */ - typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) - { - Warn.onOverflow!x(lhs); - assert(0); - } - /// ditto - typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - Warn.onOverflow!x(lhs, rhs); - assert(0); - } -} - -@safe unittest -{ - void test(T)() - { - Checked!(int, Abort) x; - x = 42; - auto x1 = cast(T) x; - assert(x1 == 42); - //x1 += long(int.max); - } - test!short; - test!(const short); - test!(immutable short); -} - - -// Throw -/** - -Force all integral errors to fail by throwing an exception of type -`Throw.CheckFailure`. The message coming with the error is similar to the one -printed by `Warn`. - -*/ -struct Throw -{ - /** - Exception type thrown upon any failure. - */ - static class CheckFailure : Exception - { - this(T...)(string f, T vals) - { - import std.format : format; - super(format(f, vals)); - } - } - - /** - - Called automatically upon a bad cast (one that loses precision or attempts - to convert a negative value to an unsigned type). The source type is `Src` - and the destination type is `Dst`. - - Params: - src = The source of the cast - - Returns: Nominally the result is the desired value of the cast operation, - which will be forwarded as the result of the cast. For `Throw`, the - function never returns because it throws an exception. - - */ - static Dst onBadCast(Dst, Src)(Src src) - { - throw new CheckFailure("Erroneous cast: cast(%s) %s(%s)", - Dst.stringof, Src.stringof, src); - } - - /** - - Called automatically upon a bounds error. - - Params: - rhs = The right-hand side value in the assignment, after the operator has - been evaluated - bound = The value of the bound being violated - - Returns: Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Throw`, the function never returns because - it throws. - - */ - static T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - throw new CheckFailure("Lower bound error: %s(%s) < %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - } - /// ditto - static T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - throw new CheckFailure("Upper bound error: %s(%s) > %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - } - - /** - - Called automatically upon a comparison for equality. Throws upon an - erroneous comparison (one that would make a signed negative value appear - equal to an unsigned positive value). - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: The result of the comparison. - - Throws: `CheckFailure` if the comparison is mathematically erroneous. - - */ - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - throw new CheckFailure("Erroneous comparison: %s(%s) == %s(%s)", - L.stringof, lhs, R.stringof, rhs); - } - return result; - } - - /** - - Called automatically upon a comparison for ordering using one of the - operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. - it would make a signed negative value appear greater than or equal to an - unsigned positive value), throws a `Throw.CheckFailure` exception. - Otherwise, the three-state result is returned (positive if $(D lhs > rhs), - negative if $(D lhs < rhs), `0` otherwise). - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: For correct comparisons, returns a positive integer if $(D lhs > - rhs), a negative integer if $(D lhs < rhs), `0` if the two are equal. - - Throws: Upon a mistaken comparison such as $(D int(-1) < uint(0)), the - function never returns because it throws a `Throw.CheckedFailure` exception. - - */ - static int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"cmp"(lhs, rhs, error); - if (error) - { - throw new CheckFailure("Erroneous ordering comparison: %s(%s) and %s(%s)", - Lhs.stringof, lhs, Rhs.stringof, rhs); - } - return result; - } - - /** - - Called automatically upon an overflow during a unary or binary operation. - - Params: - x = The operator, e.g. `-` - lhs = The left-hand side (or sole) argument - rhs = The right-hand side type involved in the operator - - Returns: Nominally the result is the desired value of the operator, which - will be forwarded as result. For `Throw`, the function never returns because - it throws an exception. - - */ - static typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs) - { - throw new CheckFailure("Overflow on unary operator: %s%s(%s)", - x, Lhs.stringof, lhs); - } - /// ditto - static typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - throw new CheckFailure("Overflow on binary operator: %s(%s) %s %s(%s)", - Lhs.stringof, lhs, x, Rhs.stringof, rhs); - } -} - -/// -@safe unittest -{ - void test(T)() - { - Checked!(int, Throw) x; - x = 42; - auto x1 = cast(T) x; - assert(x1 == 42); - x = T.max + 1; - import std.exception : assertThrown, assertNotThrown; - assertThrown(cast(T) x); - x = x.max; - assertThrown(x += 42); - assertThrown(x += 42L); - x = x.min; - assertThrown(-x); - assertThrown(x -= 42); - assertThrown(x -= 42L); - x = -1; - assertNotThrown(x == -1); - assertThrown(x == uint(-1)); - assertNotThrown(x <= -1); - assertThrown(x <= uint(-1)); - } - test!short; - test!(const short); - test!(immutable short); -} - -// Warn -/** -Hook that prints to `stderr` a trace of all integral errors, without affecting -default behavior. -*/ -struct Warn -{ - import std.stdio : writefln; -static: - /** - - Called automatically upon a bad cast from `src` to type `Dst` (one that - loses precision or attempts to convert a negative value to an unsigned - type). - - Params: - src = The source of the cast - Dst = The target type of the cast - - Returns: `cast(Dst) src` - - */ - Dst onBadCast(Dst, Src)(Src src) - { - trustedStderr.writefln("Erroneous cast: cast(%s) %s(%s)", - Dst.stringof, Src.stringof, src); - return cast(Dst) src; - } - - /** - - Called automatically upon a bad `opOpAssign` call (one that loses precision - or attempts to convert a negative value to an unsigned type). - - Params: - rhs = The right-hand side value in the assignment, after the operator has - been evaluated - bound = The bound being violated - - Returns: `cast(T) rhs` - */ - T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - trustedStderr.writefln("Lower bound error: %s(%s) < %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - return cast(T) rhs; - } - /// ditto - T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - trustedStderr.writefln("Upper bound error: %s(%s) > %s(%s)", - Rhs.stringof, rhs, T.stringof, bound); - return cast(T) rhs; - } - - /** - - Called automatically upon a comparison for equality. In case of an Erroneous - comparison (one that would make a signed negative value appear equal to an - unsigned positive value), writes a warning message to `stderr` as a side - effect. - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: In all cases the function returns the built-in result of $(D lhs == - rhs). - - */ - bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - trustedStderr.writefln("Erroneous comparison: %s(%s) == %s(%s)", - Lhs.stringof, lhs, Rhs.stringof, rhs); - return lhs == rhs; - } - return result; - } - - /// - @safe unittest - { - auto x = checked!Warn(-42); - // Passes - assert(x == -42); - // Passes but prints a warning - // assert(x == uint(-42)); - } - - /** - - Called automatically upon a comparison for ordering using one of the - operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e. - it would make a signed negative value appear greater than or equal to an - unsigned positive value), then a warning message is printed to `stderr`. - - Params: - lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - rhs = The right-hand side type involved in the operator - - Returns: In all cases, returns $(D lhs < rhs ? -1 : lhs > rhs). The result - is not autocorrected in case of an erroneous comparison. - - */ - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - bool error; - auto result = opChecked!"cmp"(lhs, rhs, error); - if (error) - { - trustedStderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)", - Lhs.stringof, lhs, Rhs.stringof, rhs); - return lhs < rhs ? -1 : lhs > rhs; - } - return result; - } - - /// - @safe unittest - { - auto x = checked!Warn(-42); - // Passes - assert(x <= -42); - // Passes but prints a warning - // assert(x <= uint(-42)); - } - - /** - - Called automatically upon an overflow during a unary or binary operation. - - Params: - x = The operator involved - Lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of - the operator is `Checked!int` - Rhs = The right-hand side type involved in the operator - - Returns: $(D mixin(x ~ "lhs")) for unary, $(D mixin("lhs" ~ x ~ "rhs")) for - binary - - */ - typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) - { - trustedStderr.writefln("Overflow on unary operator: %s%s(%s)", - x, Lhs.stringof, lhs); - return mixin(x ~ "lhs"); - } - /// ditto - typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - trustedStderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)", - Lhs.stringof, lhs, x, Rhs.stringof, rhs); - static if (x == "/") // Issue 20743: mixin below would cause SIGFPE on POSIX - return typeof(lhs / rhs).min; // or EXCEPTION_INT_OVERFLOW on Windows - else - return mixin("lhs" ~ x ~ "rhs"); - } - - // This is safe because we do not assign to the reference returned by - // `stderr`. The ability for the caller to do that is why `stderr` is not - // safe in the general case. - private @property auto ref trustedStderr() @trusted - { - import std.stdio : stderr; - - return stderr; - } -} - -/// -@safe unittest -{ - auto x = checked!Warn(42); - short x1 = cast(short) x; - //x += long(int.max); - auto y = checked!Warn(cast(const int) 42); - short y1 = cast(const byte) y; -} - -@system unittest -{ - auto a = checked!Warn(int.min); - auto b = checked!Warn(-1); - auto x = checked!Abort(int.min); - auto y = checked!Abort(-1); - - // Temporarily redirect output to stderr to make sure we get the right output. - import std.file : exists, remove; - import std.process : uniqueTempPath; - import std.stdio : stderr; - auto tmpname = uniqueTempPath; - scope(exit) if (exists(tmpname)) remove(tmpname); - auto t = stderr; - stderr.open(tmpname, "w"); - // Open a new scope to minimize code ran with stderr redirected. - { - scope(exit) stderr = t; - assert(a / b == a * b); - import std.exception : assertThrown; - import core.exception : AssertError; - assertThrown!AssertError(x / y); - } - import std.file : readText; - import std.ascii : newline; - auto witness = readText(tmpname); - auto expected = -"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline ~ -"Overflow on binary operator: int(-2147483648) * const(int)(-1)" ~ newline ~ -"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline; - assert(witness == expected, "'" ~ witness ~ "'"); -} - -// https://issues.dlang.org/show_bug.cgi?id=22249 -@safe unittest -{ - alias _ = Warn.onLowerBound!(int, int); -} - -// ProperCompare -/** - -Hook that provides arithmetically correct comparisons for equality and ordering. -Comparing an object of type $(D Checked!(X, ProperCompare)) against another -integral (for equality or ordering) ensures that no surprising conversions from -signed to unsigned integral occur before the comparison. Using $(D Checked!(X, -ProperCompare)) on either side of a comparison for equality against a -floating-point number makes sure the integral can be properly converted to the -floating point type, thus making sure equality is transitive. - -*/ -struct ProperCompare -{ - /** - Hook for `==` and `!=` that ensures comparison against integral values has - the behavior expected by the usual arithmetic rules. The built-in semantics - yield surprising behavior when comparing signed values against unsigned - values for equality, for example $(D uint.max == -1) or $(D -1_294_967_296 == - 3_000_000_000u). The call $(D hookOpEquals(x, y)) returns `true` if and only - if `x` and `y` represent the same arithmetic number. - - If one of the numbers is an integral and the other is a floating-point - number, $(D hookOpEquals(x, y)) returns `true` if and only if the integral - can be converted exactly (without approximation) to the floating-point - number. This is in order to preserve transitivity of equality: if $(D - hookOpEquals(x, y)) and $(D hookOpEquals(y, z)) then $(D hookOpEquals(y, - z)), in case `x`, `y`, and `z` are a mix of integral and floating-point - numbers. - - Params: - lhs = The left-hand side of the comparison for equality - rhs = The right-hand side of the comparison for equality - - Returns: - The result of the comparison, `true` if the values are equal - */ - static bool hookOpEquals(L, R)(L lhs, R rhs) - { - alias C = typeof(lhs + rhs); - static if (isFloatingPoint!C) - { - static if (!isFloatingPoint!L) - { - return hookOpEquals(rhs, lhs); - } - else static if (!isFloatingPoint!R) - { - static assert(isFloatingPoint!L && !isFloatingPoint!R); - auto rhs1 = C(rhs); - return lhs == rhs1 && cast(R) rhs1 == rhs; - } - else - return lhs == rhs; - } - else - { - bool error; - auto result = opChecked!"=="(lhs, rhs, error); - if (error) - { - // Only possible error is a wrong "true" - return false; - } - return result; - } - } - - /** - Hook for `<`, `<=`, `>`, and `>=` that ensures comparison against integral - values has the behavior expected by the usual arithmetic rules. The built-in - semantics yield surprising behavior when comparing signed values against - unsigned values, for example $(D 0u < -1). The call $(D hookOpCmp(x, y)) - returns `-1` if and only if `x` is smaller than `y` in abstract arithmetic - sense. - - If one of the numbers is an integral and the other is a floating-point - number, $(D hookOpEquals(x, y)) returns a floating-point number that is `-1` - if `x < y`, `0` if `x == y`, `1` if `x > y`, and `NaN` if the floating-point - number is `NaN`. - - Params: - lhs = The left-hand side of the comparison for ordering - rhs = The right-hand side of the comparison for ordering - - Returns: - The result of the comparison (negative if $(D lhs < rhs), positive if $(D - lhs > rhs), `0` if the values are equal) - */ - static auto hookOpCmp(L, R)(L lhs, R rhs) - { - alias C = typeof(lhs + rhs); - static if (isFloatingPoint!C) - { - return lhs < rhs - ? C(-1) - : lhs > rhs ? C(1) : lhs == rhs ? C(0) : C.init; - } - else - { - static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) - { - static assert(isUnsigned!C); - static assert(isUnsigned!L != isUnsigned!R); - if (!isUnsigned!L && lhs < 0) - return -1; - if (!isUnsigned!R && rhs < 0) - return 1; - } - return lhs < rhs ? -1 : lhs > rhs; - } - } -} - -/// -@safe unittest -{ - alias opEqualsProper = ProperCompare.hookOpEquals; - assert(opEqualsProper(42, 42)); - assert(opEqualsProper(42.0, 42.0)); - assert(opEqualsProper(42u, 42)); - assert(opEqualsProper(42, 42u)); - assert(-1 == 4294967295u); - assert(!opEqualsProper(-1, 4294967295u)); - assert(!opEqualsProper(const uint(-1), -1)); - assert(!opEqualsProper(uint(-1), -1.0)); - assert(3_000_000_000U == -1_294_967_296); - assert(!opEqualsProper(3_000_000_000U, -1_294_967_296)); -} - -@safe unittest -{ - alias opCmpProper = ProperCompare.hookOpCmp; - assert(opCmpProper(42, 42) == 0); - assert(opCmpProper(42, 42.0) == 0); - assert(opCmpProper(41, 42.0) < 0); - assert(opCmpProper(42, 41.0) > 0); - import std.math.traits : isNaN; - assert(isNaN(opCmpProper(41, double.init))); - assert(opCmpProper(42u, 42) == 0); - assert(opCmpProper(42, 42u) == 0); - assert(opCmpProper(-1, uint(-1)) < 0); - assert(opCmpProper(uint(-1), -1) > 0); - assert(opCmpProper(-1.0, -1) == 0); -} - -@safe unittest -{ - auto x1 = Checked!(uint, ProperCompare)(42u); - assert(x1.get < -1); - assert(x1 > -1); -} - -// WithNaN -/** - -Hook that reserves a special value as a "Not a Number" representative. For -signed integrals, the reserved value is `T.min`. For signed integrals, the -reserved value is `T.max`. - -The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must -be taken that all variables are explicitly initialized. Any arithmetic and logic -operation involving at least on NaN becomes NaN itself. All of $(D a == b), $(D -a < b), $(D a > b), $(D a <= b), $(D a >= b) yield `false` if at least one of -`a` and `b` is NaN. - -*/ -struct WithNaN -{ -static: - /** - The default value used for values not explicitly initialized. It is the NaN - value, i.e. `T.min` for signed integrals and `T.max` for unsigned integrals. - */ - enum T defaultValue(T) = T.min == 0 ? T.max : T.min; - /** - The maximum value representable is `T.max` for signed integrals, $(D - T.max - 1) for unsigned integrals. The minimum value representable is $(D - T.min + 1) for signed integrals, `0` for unsigned integrals. - */ - enum T max(T) = cast(T) (T.min == 0 ? T.max - 1 : T.max); - /// ditto - enum T min(T) = cast(T) (T.min == 0 ? T(0) : T.min + 1); - - /** - If `rhs` is `WithNaN.defaultValue!Rhs`, returns - `WithNaN.defaultValue!Lhs`. Otherwise, returns $(D cast(Lhs) rhs). - - Params: - rhs = the value being cast (`Rhs` is the first argument to `Checked`) - Lhs = the target type of the cast - - Returns: The result of the cast operation. - */ - Lhs hookOpCast(Lhs, Rhs)(Rhs rhs) - { - static if (is(Lhs == bool)) - { - return rhs != defaultValue!Rhs && rhs != 0; - } - else static if (valueConvertible!(Rhs, Lhs)) - { - return rhs != defaultValue!Rhs ? Lhs(rhs) : defaultValue!Lhs; - } - else - { - // Not value convertible, only viable option is rhs fits within the - // bounds of Lhs - static if (ProperCompare.hookOpCmp(Rhs.min, Lhs.min) < 0) - { - // Example: hookOpCast!short(int(42)), hookOpCast!uint(int(42)) - if (ProperCompare.hookOpCmp(rhs, Lhs.min) < 0) - return defaultValue!Lhs; - } - static if (ProperCompare.hookOpCmp(Rhs.max, Lhs.max) > 0) - { - // Example: hookOpCast!int(uint(42)) - if (ProperCompare.hookOpCmp(rhs, Lhs.max) > 0) - return defaultValue!Lhs; - } - return cast(Lhs) rhs; - } - } - - /// - @safe unittest - { - auto x = checked!WithNaN(422); - assert((cast(ubyte) x) == 255); - x = checked!WithNaN(-422); - assert((cast(byte) x) == -128); - assert(cast(short) x == -422); - assert(cast(bool) x); - x = x.init; // set back to NaN - assert(x != true); - assert(x != false); - } - - /** - - Returns `false` if $(D lhs == WithNaN.defaultValue!Lhs), $(D lhs == rhs) - otherwise. - - Params: - lhs = The left-hand side of the comparison (`Lhs` is the first argument to - `Checked`) - rhs = The right-hand side of the comparison - - Returns: `lhs != WithNaN.defaultValue!Lhs && lhs == rhs` - */ - bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - return lhs != defaultValue!Lhs && lhs == rhs; - } - - /** - - If $(D lhs == WithNaN.defaultValue!Lhs), returns `double.init`. Otherwise, - has the same semantics as the default comparison. - - Params: - lhs = The left-hand side of the comparison (`Lhs` is the first argument to - `Checked`) - rhs = The right-hand side of the comparison - - Returns: `double.init` if `lhs == WitnNaN.defaultValue!Lhs`, `-1.0` if $(D - lhs < rhs), `0.0` if $(D lhs == rhs), `1.0` if $(D lhs > rhs). - - */ - double hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - if (lhs == defaultValue!Lhs) return double.init; - return lhs < rhs - ? -1.0 - : lhs > rhs ? 1.0 : lhs == rhs ? 0.0 : double.init; - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - assert(!(x < 0) && !(x > 0) && !(x == 0)); - x = 1; - assert(x > 0 && !(x < 0) && !(x == 0)); - } - - /** - Defines hooks for unary operators `-`, `~`, `++`, and `--`. - - For `-` and `~`, if $(D v == WithNaN.defaultValue!T), returns - `WithNaN.defaultValue!T`. Otherwise, the semantics is the same as for the - built-in operator. - - For `++` and `--`, if $(D v == WithNaN.defaultValue!Lhs) or the operation - would result in an overflow, sets `v` to `WithNaN.defaultValue!T`. - Otherwise, the semantics is the same as for the built-in operator. - - Params: - x = The operator symbol - v = The left-hand side of the comparison (`T` is the first argument to - `Checked`) - - Returns: $(UL $(LI For $(D x == "-" || x == "~"): If $(D v == - WithNaN.defaultValue!T), the function returns `WithNaN.defaultValue!T`. - Otherwise it returns the normal result of the operator.) $(LI For $(D x == - "++" || x == "--"): The function returns `void`.)) - - */ - auto hookOpUnary(string x, T)(ref T v) - { - static if (x == "-" || x == "~") - { - return v != defaultValue!T ? mixin(x ~ "v") : v; - } - else static if (x == "++") - { - static if (defaultValue!T == T.min) - { - if (v != defaultValue!T) - { - if (v == T.max) v = defaultValue!T; - else ++v; - } - } - else - { - static assert(defaultValue!T == T.max); - if (v != defaultValue!T) ++v; - } - } - else static if (x == "--") - { - if (v != defaultValue!T) --v; - } - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - ++x; - assert(x.isNaN); - x = 1; - assert(!x.isNaN); - x = -x; - ++x; - assert(!x.isNaN); - } - - @safe unittest // for coverage - { - Checked!(uint, WithNaN) y; - ++y; - assert(y.isNaN); - } - - /** - Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, - `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the - left-hand side operand. If $(D lhs == WithNaN.defaultValue!Lhs), returns - $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the - operand. Otherwise, evaluates the operand. If evaluation does not overflow, - returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + - rhs))). - - Params: - x = The operator symbol - lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) - rhs = The right-hand side operand - - Returns: If $(D lhs != WithNaN.defaultValue!Lhs) and the operator does not - overflow, the function returns the same result as the built-in operator. In - all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). - */ - auto hookOpBinary(string x, L, R)(L lhs, R rhs) - { - alias Result = typeof(lhs + rhs); - if (lhs != defaultValue!L) - { - bool error; - auto result = opChecked!x(lhs, rhs, error); - if (!error) return result; - } - return defaultValue!Result; - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - assert((x + 1).isNaN); - x = 100; - assert(!(x + 1).isNaN); - } - - /** - Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, - `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the - right-hand side operand. If $(D rhs == WithNaN.defaultValue!Rhs), returns - $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the - operand. Otherwise, evaluates the operand. If evaluation does not overflow, - returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs + - rhs))). - - Params: - x = The operator symbol - lhs = The left-hand side operand - rhs = The right-hand side operand (`Rhs` is the first argument to `Checked`) - - Returns: If $(D rhs != WithNaN.defaultValue!Rhs) and the operator does not - overflow, the function returns the same result as the built-in operator. In - all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))). - */ - auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs) - { - alias Result = typeof(lhs + rhs); - if (rhs != defaultValue!R) - { - bool error; - auto result = opChecked!x(lhs, rhs, error); - if (!error) return result; - } - return defaultValue!Result; - } - /// - @safe unittest - { - Checked!(int, WithNaN) x; - assert((1 + x).isNaN); - x = 100; - assert(!(1 + x).isNaN); - } - - /** - - Defines hooks for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, - `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` for cases where a `Checked` - object is the left-hand side operand. If $(D lhs == - WithNaN.defaultValue!Lhs), no action is carried. Otherwise, evaluates the - operand. If evaluation does not overflow and fits in `Lhs` without loss of - information or change of sign, sets `lhs` to the result. Otherwise, sets - `lhs` to `WithNaN.defaultValue!Lhs`. - - Params: - x = The operator symbol (without the `=`) - lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`) - rhs = The right-hand side operand - - Returns: `void` - */ - void hookOpOpAssign(string x, L, R)(ref L lhs, R rhs) - { - if (lhs == defaultValue!L) - return; - bool error; - auto temp = opChecked!x(lhs, rhs, error); - lhs = error - ? defaultValue!L - : hookOpCast!L(temp); - } - - /// - @safe unittest - { - Checked!(int, WithNaN) x; - x += 4; - assert(x.isNaN); - x = 0; - x += 4; - assert(!x.isNaN); - x += int.max; - assert(x.isNaN); - } -} - -/// -@safe unittest -{ - auto x1 = Checked!(int, WithNaN)(); - assert(x1.isNaN); - assert(x1.get == int.min); - assert(x1 != x1); - assert(!(x1 < x1)); - assert(!(x1 > x1)); - assert(!(x1 == x1)); - ++x1; - assert(x1.isNaN); - assert(x1.get == int.min); - --x1; - assert(x1.isNaN); - assert(x1.get == int.min); - x1 = 42; - assert(!x1.isNaN); - assert(x1 == x1); - assert(x1 <= x1); - assert(x1 >= x1); - static assert(x1.min == int.min + 1); - x1 += long(int.max); -} - -/** -Queries whether a $(D Checked!(T, WithNaN)) object is not a number (NaN). - -Params: x = the `Checked` instance queried - -Returns: `true` if `x` is a NaN, `false` otherwise -*/ -bool isNaN(T)(const Checked!(T, WithNaN) x) -{ - return x.get == x.init.get; -} - -/// -@safe unittest -{ - auto x1 = Checked!(int, WithNaN)(); - assert(x1.isNaN); - x1 = 1; - assert(!x1.isNaN); - x1 = x1.init; - assert(x1.isNaN); -} - -@safe unittest -{ - void test1(T)() - { - auto x1 = Checked!(T, WithNaN)(); - assert(x1.isNaN); - assert(x1.get == int.min); - assert(x1 != x1); - assert(!(x1 < x1)); - assert(!(x1 > x1)); - assert(!(x1 == x1)); - assert(x1.get == int.min); - auto x2 = Checked!(T, WithNaN)(42); - assert(!x2.isNaN); - assert(x2 == x2); - assert(x2 <= x2); - assert(x2 >= x2); - static assert(x2.min == T.min + 1); - } - test1!int; - test1!(const int); - test1!(immutable int); - - void test2(T)() - { - auto x1 = Checked!(T, WithNaN)(); - assert(x1.get == T.min); - assert(x1 != x1); - assert(!(x1 < x1)); - assert(!(x1 > x1)); - assert(!(x1 == x1)); - ++x1; - assert(x1.get == T.min); - --x1; - assert(x1.get == T.min); - x1 = 42; - assert(x1 == x1); - assert(x1 <= x1); - assert(x1 >= x1); - static assert(x1.min == T.min + 1); - x1 += long(T.max); - } - test2!int; -} - -@safe unittest -{ - alias Smart(T) = Checked!(Checked!(T, ProperCompare), WithNaN); - Smart!int x1; - assert(x1 != x1); - x1 = -1; - assert(x1 < 1u); - auto x2 = Smart!(const int)(42); -} - -// Saturate -/** - -Hook that implements $(I saturation), i.e. any arithmetic operation that would -overflow leaves the result at its extreme value (`min` or `max` depending on the -direction of the overflow). - -Saturation is not sticky; if a value reaches its saturation value, another -operation may take it back to normal range. - -*/ -struct Saturate -{ -static: - /** - - Implements saturation for operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, - and `>>>=`. This hook is called if the result of the binary operation does - not fit in `Lhs` without loss of information or a change in sign. - - Params: - Rhs = The right-hand side type in the assignment, after the operation has - been computed - bound = The bound being violated - - Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise. - - */ - T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - return bound; - } - /// ditto - T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - return bound; - } - /// - @safe unittest - { - auto x = checked!Saturate(short(100)); - x += 33000; - assert(x == short.max); - x -= 70000; - assert(x == short.min); - } - - /** - - Implements saturation for operators `+`, `-` (unary and binary), `*`, `/`, - `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`. - - For unary `-`, `onOverflow` is called if $(D lhs == Lhs.min) and `Lhs` is a - signed type. The function returns `Lhs.max`. - - For binary operators, the result is as follows: $(UL $(LI `Lhs.max` if the - result overflows in the positive direction, on division by `0`, or on - shifting right by a negative value) $(LI `Lhs.min` if the result overflows - in the negative direction) $(LI `0` if `lhs` is being shifted left by a - negative value, or shifted right by a large positive value)) - - Params: - x = The operator involved in the `opAssign` operation - Lhs = The left-hand side of the operator (`Lhs` is the first argument to - `Checked`) - Rhs = The right-hand side type in the operator - - Returns: The saturated result of the operator. - - */ - auto onOverflow(string x, Lhs)(Lhs lhs) - { - static assert(x == "-" || x == "++" || x == "--"); - return x == "--" ? Lhs.min : Lhs.max; - } - /// ditto - typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - static if (x == "+") - return rhs >= 0 ? Lhs.max : Lhs.min; - else static if (x == "*") - return (lhs >= 0) == (rhs >= 0) ? Lhs.max : Lhs.min; - else static if (x == "^^") - return lhs > 0 || !(rhs & 1) ? Lhs.max : Lhs.min; - else static if (x == "-") - return rhs >= 0 ? Lhs.min : Lhs.max; - else static if (x == "/" || x == "%") - return Lhs.max; - else static if (x == "<<") - return rhs >= 0 ? Lhs.max : 0; - else static if (x == ">>" || x == ">>>") - return rhs >= 0 ? 0 : Lhs.max; - else - static assert(false); - } - /// - @safe unittest - { - assert(checked!Saturate(int.max) + 1 == int.max); - assert(checked!Saturate(100) ^^ 10 == int.max); - assert(checked!Saturate(-100) ^^ 10 == int.max); - assert(checked!Saturate(100) / 0 == int.max); - assert(checked!Saturate(100) << -1 == 0); - assert(checked!Saturate(100) << 33 == int.max); - assert(checked!Saturate(100) >> -1 == int.max); - assert(checked!Saturate(100) >> 33 == 0); - } -} - -/// -@safe unittest -{ - auto x = checked!Saturate(int.max); - ++x; - assert(x == int.max); - --x; - assert(x == int.max - 1); - x = int.min; - assert(-x == int.max); - x -= 42; - assert(x == int.min); - assert(x * -2 == int.max); -} - -/* -Yields `true` if `T1` is "value convertible" (by C's "value preserving" rule, -see $(HTTP c-faq.com/expr/preservingrules.html)) to `T2`, where the two are -integral types. That is, all of values in `T1` are also in `T2`. For example -`int` is value convertible to `long` but not to `uint` or `ulong`. -*/ -private enum valueConvertible(T1, T2) = isIntegral!T1 && isIntegral!T2 && - is(T1 : T2) && ( - isUnsigned!T1 == isUnsigned!T2 || // same signedness - !isUnsigned!T2 && T2.sizeof > T1.sizeof // safely convertible - ); /** - -Defines binary operations with overflow checking for any two integral types. -The result type obeys the language rules (even when they may be -counterintuitive), and `overflow` is set if an overflow occurs (including -inadvertent change of signedness, e.g. `-1` is converted to `uint`). -Conceptually the behavior is: - -$(OL $(LI Perform the operation in infinite precision) -$(LI If the infinite-precision result fits in the result type, return it and -do not touch `overflow`) -$(LI Otherwise, set `overflow` to `true` and return an unspecified value) -) - -The implementation exploits properties of types and operations to minimize -additional work. - -Params: -x = The binary operator involved, e.g. `/` -lhs = The left-hand side of the operator -rhs = The right-hand side of the operator -overflow = The overflow indicator (assigned `true` in case there's an error) - -Returns: -The result of the operation, which is the same as the built-in operator -*/ -typeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()"))) -opChecked(string x, L, R)(const L lhs, const R rhs, ref bool overflow) -if (isIntegral!L && isIntegral!R) -{ - static if (x == "cmp") - alias Result = int; - else - alias Result = typeof(mixin("L() " ~ x ~ " R()")); - - import core.checkedint : addu, adds, subs, muls, subu, mulu; - import std.algorithm.comparison : among; - static if (x == "==") - { - alias C = typeof(lhs + rhs); - static if (valueConvertible!(L, C) && valueConvertible!(R, C)) - { - // Values are converted to R before comparison, cool. - return lhs == rhs; - } - else - { - static assert(isUnsigned!C); - static assert(isUnsigned!L != isUnsigned!R); - if (lhs != rhs) return false; - // R(lhs) and R(rhs) have the same bit pattern, yet may be - // different due to signedness change. - static if (!isUnsigned!R) - { - if (rhs >= 0) - return true; - } - else - { - if (lhs >= 0) - return true; - } - overflow = true; - return true; - } - } - else static if (x == "cmp") - { - alias C = typeof(lhs + rhs); - static if (!valueConvertible!(L, C) || !valueConvertible!(R, C)) - { - static assert(isUnsigned!C); - static assert(isUnsigned!L != isUnsigned!R); - if (!isUnsigned!L && lhs < 0) - { - overflow = true; - return -1; - } - if (!isUnsigned!R && rhs < 0) - { - overflow = true; - return 1; - } - } - return lhs < rhs ? -1 : lhs > rhs; - } - else static if (x.among("<<", ">>", ">>>")) - { - // Handle shift separately from all others. The test below covers - // negative rhs as well. - import std.conv : unsigned; - if (unsigned(rhs) > 8 * Result.sizeof) goto fail; - return mixin("lhs" ~ x ~ "rhs"); - } - else static if (x.among("&", "|", "^")) - { - // Nothing to check - return mixin("lhs" ~ x ~ "rhs"); - } - else static if (x == "^^") - { - // Exponentiation is weird, handle separately - return pow(lhs, rhs, overflow); - } - else static if (valueConvertible!(L, Result) && - valueConvertible!(R, Result)) - { - static if (L.sizeof < Result.sizeof && R.sizeof < Result.sizeof && - x.among("+", "-", "*")) - { - // No checks - both are value converted and result is in range - return mixin("lhs" ~ x ~ "rhs"); - } - else static if (x == "+") - { - static if (isUnsigned!Result) alias impl = addu; - else alias impl = adds; - return impl(Result(lhs), Result(rhs), overflow); - } - else static if (x == "-") - { - static if (isUnsigned!Result) alias impl = subu; - else alias impl = subs; - return impl(Result(lhs), Result(rhs), overflow); - } - else static if (x == "*") - { - static if (!isUnsigned!L && !isUnsigned!R && - is(L == Result)) - { - if (lhs == Result.min && rhs == -1) goto fail; - } - static if (isUnsigned!Result) alias impl = mulu; - else alias impl = muls; - return impl(Result(lhs), Result(rhs), overflow); - } - else static if (x == "/" || x == "%") - { - static if (!isUnsigned!L && !isUnsigned!R && - is(L == Result) && x == "/") - { - if (lhs == Result.min && rhs == -1) goto fail; - } - if (rhs == 0) goto fail; - return mixin("lhs" ~ x ~ "rhs"); - } - else static assert(0, x); - } - else // Mixed signs - { - static assert(isUnsigned!Result); - static assert(isUnsigned!L != isUnsigned!R); - static if (x == "+") - { - static if (!isUnsigned!L) - { - if (lhs < 0) - return subu(Result(rhs), Result(-lhs), overflow); - } - else static if (!isUnsigned!R) - { - if (rhs < 0) - return subu(Result(lhs), Result(-rhs), overflow); - } - return addu(Result(lhs), Result(rhs), overflow); - } - else static if (x == "-") - { - static if (!isUnsigned!L) - { - if (lhs < 0) goto fail; - } - else static if (!isUnsigned!R) - { - if (rhs < 0) - return addu(Result(lhs), Result(-rhs), overflow); - } - return subu(Result(lhs), Result(rhs), overflow); - } - else static if (x == "*") - { - static if (!isUnsigned!L) - { - if (lhs < 0) goto fail; - } - else static if (!isUnsigned!R) - { - if (rhs < 0) goto fail; - } - return mulu(Result(lhs), Result(rhs), overflow); - } - else static if (x == "/" || x == "%") - { - static if (!isUnsigned!L) - { - if (lhs < 0 || rhs == 0) goto fail; - } - else static if (!isUnsigned!R) - { - if (rhs <= 0) goto fail; - } - return mixin("Result(lhs)" ~ x ~ "Result(rhs)"); - } - else static assert(0, x); - } - debug assert(false); -fail: - overflow = true; - return Result(0); -} - -/// -@safe unittest -{ - bool overflow; - assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow); - assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow); - assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow); - assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow); - assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow); -} - -/// -@safe unittest -{ - bool overflow; - assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow); - assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow); - assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow); - assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow); -} - -@safe unittest -{ - bool overflow; - assert(opChecked!"*"(2, 3, overflow) == 6 && !overflow); - assert(opChecked!"*"(2, 3u, overflow) == 6 && !overflow); - assert(opChecked!"*"(1u, -1, overflow) == 0 && overflow); - //assert(mul(-1, 1u, overflow) == uint.max - 1 && overflow); -} - -@safe unittest -{ - bool overflow; - assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); - assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow); - assert(opChecked!"/"(6u, 3, overflow) == 2 && !overflow); - assert(opChecked!"/"(6, 3u, overflow) == 2 && !overflow); - assert(opChecked!"/"(11, 0, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"/"(6u, 0, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"/"(-6, 2u, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"/"(-6, 0u, overflow) == 0 && overflow); - overflow = false; - assert(opChecked!"cmp"(0u, -6, overflow) == 1 && overflow); - overflow = false; - assert(opChecked!"|"(1, 2, overflow) == 3 && !overflow); -} - -/* -Exponentiation function used by the implementation of operator `^^`. -*/ -private pure @safe nothrow @nogc -auto pow(L, R)(const L lhs, const R rhs, ref bool overflow) -if (isIntegral!L && isIntegral!R) -{ - if (rhs <= 1) - { - if (rhs == 0) return 1; - static if (!isUnsigned!R) - return rhs == 1 - ? lhs - : (rhs == -1 && (lhs == 1 || lhs == -1)) ? lhs : 0; - else - return lhs; - } - - typeof(lhs ^^ rhs) b = void; - static if (!isUnsigned!L && isUnsigned!(typeof(b))) - { - // Need to worry about mixed-sign stuff - if (lhs < 0) - { - if (rhs & 1) - { - if (lhs < 0) overflow = true; - return 0; - } - b = -lhs; - } - else - { - b = lhs; - } - } - else - { - b = lhs; - } - if (b == 1) return 1; - if (b == -1) return (rhs & 1) ? -1 : 1; - if (rhs > 63) - { - overflow = true; - return 0; - } - - assert((b > 1 || b < -1) && rhs > 1); - return powImpl(b, cast(uint) rhs, overflow); -} - -// Inspiration: http://www.stepanovpapers.com/PAM.pdf -pure @safe nothrow @nogc -private T powImpl(T)(T b, uint e, ref bool overflow) -if (isIntegral!T && T.sizeof >= 4) -{ - assert(e > 1); - - import core.checkedint : muls, mulu; - static if (isUnsigned!T) alias mul = mulu; - else alias mul = muls; - - T r = b; - --e; - // Loop invariant: r * (b ^^ e) is the actual result - for (;; e /= 2) - { - if (e % 2) - { - r = mul(r, b, overflow); - if (e == 1) break; - } - b = mul(b, b, overflow); - } - return r; -} - -@safe unittest -{ - static void testPow(T)(T x, uint e) - { - bool overflow; - assert(opChecked!"^^"(T(0), 0, overflow) == 1); - assert(opChecked!"^^"(-2, T(0), overflow) == 1); - assert(opChecked!"^^"(-2, T(1), overflow) == -2); - assert(opChecked!"^^"(-1, -1, overflow) == -1); - assert(opChecked!"^^"(-2, 1, overflow) == -2); - assert(opChecked!"^^"(-2, -1, overflow) == 0); - assert(opChecked!"^^"(-2, 4u, overflow) == 16); - assert(!overflow); - assert(opChecked!"^^"(-2, 3u, overflow) == 0); - assert(overflow); - overflow = false; - assert(opChecked!"^^"(3, 64u, overflow) == 0); - assert(overflow); - overflow = false; - foreach (uint i; 0 .. e) - { - assert(opChecked!"^^"(x, i, overflow) == x ^^ i); - assert(!overflow); - } - assert(opChecked!"^^"(x, e, overflow) == x ^^ e); - assert(overflow); - } - - testPow!int(3, 21); - testPow!uint(3, 21); - testPow!long(3, 40); - testPow!ulong(3, 41); -} - -version (StdUnittest) private struct CountOverflows -{ - uint calls; - auto onOverflow(string op, Lhs)(Lhs lhs) - { - ++calls; - return mixin(op ~ "lhs"); - } - auto onOverflow(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return mixin("lhs" ~ op ~ "rhs"); - } - T onLowerBound(Rhs, T)(Rhs rhs, T bound) - { - ++calls; - return cast(T) rhs; - } - T onUpperBound(Rhs, T)(Rhs rhs, T bound) - { - ++calls; - return cast(T) rhs; - } -} - -// opBinary -@nogc nothrow pure @safe unittest -{ - static struct CountOpBinary - { - uint calls; - auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return mixin("lhs" ~ op ~ "rhs"); - } - } - auto x = Checked!(const int, void)(42), y = Checked!(immutable int, void)(142); - assert(x + y == 184); - assert(x + 100 == 142); - assert(y - x == 100); - assert(200 - x == 158); - assert(y * x == 142 * 42); - assert(x / 1 == 42); - assert(x % 20 == 2); - - auto x1 = Checked!(int, CountOverflows)(42); - assert(x1 + 0 == 42); - assert(x1 + false == 42); - assert(is(typeof(x1 + 0.5) == double)); - assert(x1 + 0.5 == 42.5); - assert(x1.hook.calls == 0); - assert(x1 + int.max == int.max + 42); - assert(x1.hook.calls == 1); - assert(x1 * 2 == 84); - assert(x1.hook.calls == 1); - assert(x1 / 2 == 21); - assert(x1.hook.calls == 1); - assert(x1 % 20 == 2); - assert(x1.hook.calls == 1); - assert(x1 << 2 == 42 << 2); - assert(x1.hook.calls == 1); - assert(x1 << 42 == x1.get << x1.get); - assert(x1.hook.calls == 2); - x1 = int.min; - assert(x1 - 1 == int.max); - assert(x1.hook.calls == 3); - - auto x2 = Checked!(int, CountOpBinary)(42); - assert(x2 + 1 == 43); - assert(x2.hook.calls == 1); - - auto x3 = Checked!(uint, CountOverflows)(42u); - assert(x3 + 1 == 43); - assert(x3.hook.calls == 0); - assert(x3 - 1 == 41); - assert(x3.hook.calls == 0); - assert(x3 + (-42) == 0); - assert(x3.hook.calls == 0); - assert(x3 - (-42) == 84); - assert(x3.hook.calls == 0); - assert(x3 * 2 == 84); - assert(x3.hook.calls == 0); - assert(x3 * -2 == -84); - assert(x3.hook.calls == 1); - assert(x3 / 2 == 21); - assert(x3.hook.calls == 1); - assert(x3 / -2 == 0); - assert(x3.hook.calls == 2); - assert(x3 ^^ 2 == 42 * 42); - assert(x3.hook.calls == 2); - - auto x4 = Checked!(int, CountOverflows)(42); - assert(x4 + 1 == 43); - assert(x4.hook.calls == 0); - assert(x4 + 1u == 43); - assert(x4.hook.calls == 0); - assert(x4 - 1 == 41); - assert(x4.hook.calls == 0); - assert(x4 * 2 == 84); - assert(x4.hook.calls == 0); - x4 = -2; - assert(x4 + 2u == 0); - assert(x4.hook.calls == 0); - assert(x4 * 2u == -4); - assert(x4.hook.calls == 1); - - auto x5 = Checked!(int, CountOverflows)(3); - assert(x5 ^^ 0 == 1); - assert(x5 ^^ 1 == 3); - assert(x5 ^^ 2 == 9); - assert(x5 ^^ 3 == 27); - assert(x5 ^^ 4 == 81); - assert(x5 ^^ 5 == 81 * 3); - assert(x5 ^^ 6 == 81 * 9); -} - -// opBinaryRight -@nogc nothrow pure @safe unittest -{ - auto x1 = Checked!(int, CountOverflows)(42); - assert(1 + x1 == 43); - assert(true + x1 == 43); - assert(0.5 + x1 == 42.5); - auto x2 = Checked!(int, void)(42); - assert(x1 + x2 == 84); - assert(x2 + x1 == 84); -} - -// opOpAssign -@safe unittest -{ - auto x1 = Checked!(int, CountOverflows)(3); - assert((x1 += 2) == 5); - x1 *= 2_000_000_000L; - assert(x1.hook.calls == 1); - x1 *= -2_000_000_000L; - assert(x1.hook.calls == 2); - - auto x2 = Checked!(ushort, CountOverflows)(ushort(3)); - assert((x2 += 2) == 5); - assert(x2.hook.calls == 0); - assert((x2 += ushort.max) == cast(ushort) (ushort(5) + ushort.max)); - assert(x2.hook.calls == 1); - - auto x3 = Checked!(uint, CountOverflows)(3u); - x3 *= ulong(2_000_000_000); - assert(x3.hook.calls == 1); -} - -// opAssign -@safe unittest -{ - Checked!(int, void) x; - x = 42; - assert(x.get == 42); - x = x; - assert(x.get == 42); - x = short(43); - assert(x.get == 43); - x = ushort(44); - assert(x.get == 44); -} - -@safe unittest -{ - static assert(!is(typeof(Checked!(short, void)(ushort(42))))); - static assert(!is(typeof(Checked!(int, void)(long(42))))); - static assert(!is(typeof(Checked!(int, void)(ulong(42))))); - assert(Checked!(short, void)(short(42)).get == 42); - assert(Checked!(int, void)(ushort(42)).get == 42); -} - -// opCast -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(cast(float) Checked!(int, void)(42)) == float)); - assert(cast(float) Checked!(int, void)(42) == 42); - - assert(is(typeof(cast(long) Checked!(int, void)(42)) == long)); - assert(cast(long) Checked!(int, void)(42) == 42); - static assert(is(typeof(cast(long) Checked!(uint, void)(42u)) == long)); - assert(cast(long) Checked!(uint, void)(42u) == 42); - - auto x = Checked!(int, void)(42); - if (x) {} else assert(0); - x = 0; - if (x) assert(0); - - static struct Hook1 - { - uint calls; - Dst hookOpCast(Dst, Src)(Src value) - { - ++calls; - return 42; - } - } - auto y = Checked!(long, Hook1)(long.max); - assert(cast(int) y == 42); - assert(cast(uint) y == 42); - assert(y.hook.calls == 2); - - static struct Hook2 - { - uint calls; - Dst onBadCast(Dst, Src)(Src value) - { - ++calls; - return 42; - } - } - auto x1 = Checked!(uint, Hook2)(100u); - assert(cast(ushort) x1 == 100); - assert(cast(short) x1 == 100); - assert(cast(float) x1 == 100); - assert(cast(double) x1 == 100); - assert(cast(real) x1 == 100); - assert(x1.hook.calls == 0); - assert(cast(int) x1 == 100); - assert(x1.hook.calls == 0); - x1 = uint.max; - assert(cast(int) x1 == 42); - assert(x1.hook.calls == 1); - - auto x2 = Checked!(int, Hook2)(-100); - assert(cast(short) x2 == -100); - assert(cast(ushort) x2 == 42); - assert(cast(uint) x2 == 42); - assert(cast(ulong) x2 == 42); - assert(x2.hook.calls == 3); -} - -// opEquals -@nogc nothrow pure @safe unittest -{ - assert(Checked!(int, void)(42) == 42L); - assert(42UL == Checked!(int, void)(42)); - - static struct Hook1 - { - uint calls; - bool hookOpEquals(Lhs, Rhs)(const Lhs lhs, const Rhs rhs) - { - ++calls; - return lhs != rhs; - } - } - auto x1 = Checked!(int, Hook1)(100); - assert(x1 != Checked!(long, Hook1)(100)); - assert(x1.hook.calls == 1); - assert(x1 != 100u); - assert(x1.hook.calls == 2); - - static struct Hook2 - { - uint calls; - bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return false; - } - } - auto x2 = Checked!(int, Hook2)(-100); - assert(x2 != x1); - // For coverage: lhs has no hookOpEquals, rhs does - assert(Checked!(uint, void)(100u) != x2); - // For coverage: different types, neither has a hookOpEquals - assert(Checked!(uint, void)(100u) == Checked!(int, void*)(100)); - assert(x2.hook.calls == 0); - assert(x2 != -100); - assert(x2.hook.calls == 1); - assert(x2 != cast(uint) -100); - assert(x2.hook.calls == 2); - x2 = 100; - assert(x2 != cast(uint) 100); - assert(x2.hook.calls == 3); - x2 = -100; - - auto x3 = Checked!(uint, Hook2)(100u); - assert(x3 != 100); - x3 = uint.max; - assert(x3 != -1); - - assert(x2 != x3); -} - -// opCmp -@nogc nothrow pure @safe unittest -{ - Checked!(int, void) x; - assert(x <= x); - assert(x < 45); - assert(x < 45u); - assert(x > -45); - assert(x < 44.2); - assert(x > -44.2); - assert(!(x < double.init)); - assert(!(x > double.init)); - assert(!(x <= double.init)); - assert(!(x >= double.init)); - - static struct Hook1 - { - uint calls; - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return 0; - } - } - auto x1 = Checked!(int, Hook1)(42); - assert(!(x1 < 43u)); - assert(!(43u < x1)); - assert(x1.hook.calls == 2); - - static struct Hook2 - { - uint calls; - int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) - { - ++calls; - return ProperCompare.hookOpCmp(lhs, rhs); - } - } - auto x2 = Checked!(int, Hook2)(-42); - assert(x2 < 43u); - assert(43u > x2); - assert(x2.hook.calls == 2); - x2 = 42; - assert(x2 > 41u); - - auto x3 = Checked!(uint, Hook2)(42u); - assert(x3 > 41); - assert(x3 > -41); -} - -// opUnary -@nogc nothrow pure @safe unittest -{ - auto x = Checked!(int, void)(42); - assert(x == +x); - static assert(is(typeof(-x) == typeof(x))); - assert(-x == Checked!(int, void)(-42)); - static assert(is(typeof(~x) == typeof(x))); - assert(~x == Checked!(int, void)(~42)); - assert(++x == 43); - assert(--x == 42); - - static struct Hook1 - { - uint calls; - auto hookOpUnary(string op, T)(T value) if (op == "-") - { - ++calls; - return T(42); - } - auto hookOpUnary(string op, T)(T value) if (op == "~") - { - ++calls; - return T(43); - } - } - auto x1 = Checked!(int, Hook1)(100); - assert(is(typeof(-x1) == typeof(x1))); - assert(-x1 == Checked!(int, Hook1)(42)); - assert(is(typeof(~x1) == typeof(x1))); - assert(~x1 == Checked!(int, Hook1)(43)); - assert(x1.hook.calls == 2); - - static struct Hook2 - { - uint calls; - void hookOpUnary(string op, T)(ref T value) if (op == "++") - { - ++calls; - --value; - } - void hookOpUnary(string op, T)(ref T value) if (op == "--") - { - ++calls; - ++value; - } - } - auto x2 = Checked!(int, Hook2)(100); - assert(++x2 == 99); - assert(x2 == 99); - assert(--x2 == 100); - assert(x2 == 100); - - auto x3 = Checked!(int, CountOverflows)(int.max - 1); - assert(++x3 == int.max); - assert(x3.hook.calls == 0); - assert(++x3 == int.min); - assert(x3.hook.calls == 1); - assert(-x3 == int.min); - assert(x3.hook.calls == 2); - - x3 = int.min + 1; - assert(--x3 == int.min); - assert(x3.hook.calls == 2); - assert(--x3 == int.max); - assert(x3.hook.calls == 3); -} - -// -@nogc nothrow pure @safe unittest -{ - Checked!(int, void) x; - assert(x == x); - assert(x == +x); - assert(x == -x); - ++x; - assert(x == 1); - x++; - assert(x == 2); - - x = 42; - assert(x == 42); - const short _short = 43; - x = _short; - assert(x == _short); - ushort _ushort = 44; - x = _ushort; - assert(x == _ushort); - assert(x == 44.0); - assert(x != 44.1); - assert(x < 45); - assert(x < 44.2); - assert(x > -45); - assert(x > -44.2); - - assert(cast(long) x == 44); - assert(cast(short) x == 44); - - const Checked!(uint, void) y; - assert(y <= y); - assert(y == 0); - assert(y < x); - x = -1; - assert(x > y); -} - -@nogc nothrow pure @safe unittest -{ - alias cint = Checked!(int, void); - cint a = 1, b = 2; - a += b; - assert(a == cint(3)); - - alias ccint = Checked!(cint, Saturate); - ccint c = 14; - a += c; - assert(a == cint(17)); -} - -// toHash -@safe unittest -{ - assert(checked(42).toHash() == checked(42).toHash()); - assert(checked(12).toHash() != checked(19).toHash()); - - static struct Hook1 - { - static size_t hookToHash(T)(T payload) nothrow @trusted - { - static if (size_t.sizeof == 4) - { - return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF; - } - else - { - return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF_FFFF_FFFF; - } - - } - } - - auto a = checked!Hook1(78); - auto b = checked!Hook1(78); - assert(a.toHash() == b.toHash()); - - assert(checked!Hook1(12).toHash() != checked!Hook1(13).toHash()); - - static struct Hook2 - { - static if (size_t.sizeof == 4) - { - static size_t hashMask = 0xFFFF_0000; - } - else - { - static size_t hashMask = 0xFFFF_0000_FFFF_0000; - } - - static size_t hookToHash(T)(T payload) nothrow @trusted - { - return typeid(payload).getHash(&payload) ^ hashMask; - } - } - - auto x = checked!Hook2(1901); - auto y = checked!Hook2(1989); - - assert((() nothrow @safe => x.toHash() == x.toHash())()); - - assert(x.toHash() == x.toHash()); - assert(x.toHash() != y.toHash()); - assert(checked!Hook1(1901).toHash() != x.toHash()); - - immutable z = checked!Hook1(1901); - immutable t = checked!Hook1(1901); - immutable w = checked!Hook2(1901); - - assert(z.toHash() == t.toHash()); - assert(z.toHash() != x.toHash()); - assert(z.toHash() != w.toHash()); - - const long c = 0xF0F0F0F0; - const long d = 0xF0F0F0F0; - - assert(checked!Hook1(c).toHash() != checked!Hook2(c)); - assert(checked!Hook1(c).toHash() != checked!Hook1(d)); - - // Hook with state, does not implement hookToHash - static struct Hook3 - { - ulong var1 = ulong.max; - uint var2 = uint.max; - } - - assert(checked!Hook3(12).toHash() != checked!Hook3(13).toHash()); - assert(checked!Hook3(13).toHash() == checked!Hook3(13).toHash()); - - // Hook with no state and no hookToHash, payload has its own hashing function - auto x1 = Checked!(Checked!int, ProperCompare)(123); - auto x2 = Checked!(Checked!int, ProperCompare)(123); - auto x3 = Checked!(Checked!int, ProperCompare)(144); - - assert(x1.toHash() == x2.toHash()); - assert(x1.toHash() != x3.toHash()); - assert(x2.toHash() != x3.toHash()); - - // Check shared. - { - shared shared0 = checked(12345678); - shared shared1 = checked!Hook1(123456789); - shared shared2 = checked!Hook2(234567891); - shared shared3 = checked!Hook3(345678912); - assert(shared0.toHash() == hashOf(shared0)); - assert(shared1.toHash() == hashOf(shared1)); - assert(shared2.toHash() == hashOf(shared2)); - assert(shared3.toHash() == hashOf(shared3)); - } -} - -/// -@safe unittest -{ - struct MyHook - { - static size_t hookToHash(T)(const T payload) nothrow @trusted - { - return .hashOf(payload); - } - } - - int[Checked!(int, MyHook)] aa; - Checked!(int, MyHook) var = 42; - aa[var] = 100; - - assert(aa[var] == 100); - - int[Checked!(int, Abort)] bb; - Checked!(int, Abort) var2 = 42; - bb[var2] = 100; - - assert(bb[var2] == 100); -} + * This module is now deprecated, use $(MREF std, experimental) + * instead. + * + * Copyright: Copyright The D Language Foundation 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * Source: $(PHOBOSSRC std/experimental/checkedint.d) + * + * $(SCRIPT inhibitQuickIndex = 1;) + */ +deprecated module std.experimental.checkedint; +public import std.checkedint; diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d index 08d6cbede2d..afd98add6ba 100644 --- a/libphobos/src/std/experimental/logger/core.d +++ b/libphobos/src/std/experimental/logger/core.d @@ -1633,13 +1633,14 @@ private @property Logger defaultSharedLoggerImpl() @trusted import std.concurrency : initOnce; initOnce!stdSharedDefaultLogger({ auto buffer = cast(ubyte[]) _buffer; - return emplace!FileLogger(buffer, stderr, LogLevel.all); + return emplace!FileLogger(buffer, stderr, LogLevel.warning); }()); return stdSharedDefaultLogger; } -/** This property sets and gets the default `Logger`. +/** This property sets and gets the default `Logger`. Unless set to another +logger by the user, the default logger's log level is LogLevel.warning. Example: ------------- @@ -2007,7 +2008,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto oldunspecificLogger = sharedLog; - assert(oldunspecificLogger.logLevel == LogLevel.all, + assert(oldunspecificLogger.logLevel == LogLevel.warning, to!string(oldunspecificLogger.logLevel)); assert(l.logLevel == LogLevel.all); @@ -3063,7 +3064,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); - assert(dl.logLevel == LogLevel.all); + assert(dl.logLevel == LogLevel.warning); assert(globalLogLevel == LogLevel.all); auto tl = cast(StdForwardLogger) stdThreadLocalLog; diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d index 6fd7e5ff66b..a0bea7733cf 100644 --- a/libphobos/src/std/experimental/logger/filelogger.d +++ b/libphobos/src/std/experimental/logger/filelogger.d @@ -263,7 +263,7 @@ class FileLogger : Logger { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); - assert(dl.logLevel == LogLevel.all); + assert(dl.logLevel == LogLevel.warning); assert(globalLogLevel == LogLevel.all); auto tl = cast(StdForwardLogger) stdThreadLocalLog; diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d index 0751cb86357..90bfb5820ab 100644 --- a/libphobos/src/std/experimental/logger/multilogger.d +++ b/libphobos/src/std/experimental/logger/multilogger.d @@ -191,7 +191,7 @@ class MultiLogger : Logger { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); - assert(dl.logLevel == LogLevel.all); + assert(dl.logLevel == LogLevel.warning); assert(globalLogLevel == LogLevel.all); auto tl = cast(StdForwardLogger) stdThreadLocalLog; diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index 315e054cbab..c974ada2ae8 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -89,7 +89,7 @@ import std.datetime.date : DateTime; import std.datetime.systime : Clock, SysTime, unixTimeToStdTime; import std.internal.cstring; import std.meta; -import std.range.primitives; +import std.range; import std.traits; import std.typecons; @@ -313,8 +313,7 @@ Throws: $(LREF FileException) on error. */ void[] read(R)(R name, size_t upTo = size_t.max) -if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) return readImpl(name, name.tempCString!FSChar(), upTo); @@ -356,7 +355,7 @@ version (Posix) private void[] readImpl(scope const(char)[] name, scope const(FS import core.memory : GC; import std.algorithm.comparison : min; import std.conv : to; - import std.experimental.checkedint : checked; + import std.checkedint : checked; // A few internal configuration parameters { enum size_t @@ -500,7 +499,7 @@ version (linux) @safe unittest $(REF UTFException, std, utf) on UTF decoding error. +/ S readText(S = string, R)(auto ref R name) -if (isSomeString!S && (isInputRange!R && !isInfinite!R && isSomeChar!(ElementType!R) || is(StringTypeOf!R))) +if (isSomeString!S && (isSomeFiniteCharInputRange!R || is(StringTypeOf!R))) { import std.algorithm.searching : startsWith; import std.encoding : getBOM, BOM; @@ -736,8 +735,7 @@ Throws: $(LREF FileException) on error. See_also: $(REF toFile, std,stdio) */ void write(R)(R name, const void[] buffer) -if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) && - !isConvertibleToString!R) +if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R) { static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) writeImpl(name, name.tempCString!FSChar(), buffer, false); @@ -785,8 +783,7 @@ Params: Throws: $(LREF FileException) on error. */ void append(R)(R name, const void[] buffer) -if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) && - !isConvertibleToString!R) +if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R) { static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) writeImpl(name, name.tempCString!FSChar(), buffer, true); @@ -915,10 +912,8 @@ version (Windows) private void writeImpl(scope const(char)[] name, scope const(F * Throws: $(LREF FileException) on error. */ void rename(RF, RT)(RF from, RT to) -if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) || isSomeString!RF) - && !isConvertibleToString!RF && - (isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) || isSomeString!RT) - && !isConvertibleToString!RT) +if ((isSomeFiniteCharInputRange!RF || isSomeString!RF) && !isConvertibleToString!RF && + (isSomeFiniteCharInputRange!RT || isSomeString!RT) && !isConvertibleToString!RT) { // Place outside of @trusted block auto fromz = from.tempCString!FSChar(); @@ -1027,8 +1022,7 @@ Params: Throws: $(LREF FileException) on error. */ void remove(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) removeImpl(name, name.tempCString!FSChar()); @@ -1082,7 +1076,7 @@ private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @t } version (Windows) private WIN32_FILE_ATTRIBUTE_DATA getFileAttributesWin(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)) +if (isSomeFiniteCharInputRange!R) { auto namez = name.tempCString!FSChar(); @@ -1137,8 +1131,7 @@ Throws: $(LREF FileException) on error (e.g., file not found). */ ulong getSize(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) { @@ -1233,8 +1226,7 @@ private SysTime statTimeToStdTime(char which)(ref const stat_t statbuf) void getTimes(R)(R name, out SysTime accessTime, out SysTime modificationTime) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) { @@ -1378,8 +1370,9 @@ version (StdDdoc) out SysTime fileCreationTime, out SysTime fileAccessTime, out SysTime fileModificationTime) - if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R); + if (isSomeFiniteCharInputRange!R || isConvertibleToString!R); + // above line contains both constraints for docs + // (so users know how it can be called) } else version (Windows) { @@ -1387,8 +1380,7 @@ else version (Windows) out SysTime fileCreationTime, out SysTime fileAccessTime, out SysTime fileModificationTime) - if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) + if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { import std.datetime.systime : FILETIMEToSysTime; @@ -1509,8 +1501,7 @@ private void setTimes(R)(R name, SysTime accessTime, SysTime modificationTime) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { auto namez = name.tempCString!FSChar(); static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char)) @@ -1657,8 +1648,7 @@ private void setTimesImpl(scope const(char)[] names, scope const(FSChar)* namez, $(LREF FileException) if the given file does not exist. +/ SysTime timeLastModified(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) { @@ -1742,7 +1732,7 @@ else -------------------- +/ SysTime timeLastModified(R)(R name, SysTime returnIfMissing) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R)) +if (isSomeFiniteCharInputRange!R) { version (Windows) { @@ -1902,8 +1892,7 @@ version (OSX) {} else * true if the file _name specified as input _exists */ bool exists(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { return existsImpl(name.tempCString!FSChar()); } @@ -2004,8 +1993,7 @@ private bool existsImpl(scope const(FSChar)* namez) @trusted nothrow @nogc Throws: $(LREF FileException) on error. +/ uint getAttributes(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) { @@ -2105,8 +2093,7 @@ if (isConvertibleToString!R) $(LREF FileException) on error. +/ uint getLinkAttributes(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) { @@ -2214,8 +2201,7 @@ if (isConvertibleToString!R) $(LREF FileException) if the given file does not exist. +/ void setAttributes(R)(R name, uint attributes) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) { @@ -2323,8 +2309,7 @@ if (isConvertibleToString!R) $(LREF FileException) if the given file does not exist. +/ @property bool isDir(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) { @@ -2503,8 +2488,7 @@ bool attrIsDir(uint attributes) @safe pure nothrow @nogc $(LREF FileException) if the given file does not exist. +/ @property bool isFile(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) return !name.isDir; @@ -2679,8 +2663,7 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc $(LREF FileException) if the given file does not exist. +/ @property bool isSymlink(R)(R name) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { version (Windows) return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0; @@ -2860,8 +2843,7 @@ Params: Throws: $(LREF FileException) on error. */ void chdir(R)(R pathname) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { // Place outside of @trusted block auto pathz = pathname.tempCString!FSChar(); @@ -2931,8 +2913,7 @@ Throws: if an error occured. */ void mkdir(R)(R pathname) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { // Place outside of @trusted block const pathz = pathname.tempCString!FSChar(); @@ -3135,8 +3116,7 @@ Params: Throws: $(LREF FileException) on error. */ void rmdir(R)(R pathname) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && - !isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R) { // Place outside of @trusted block auto pathz = pathname.tempCString!FSChar(); @@ -3202,15 +3182,11 @@ if (isConvertibleToString!R) exists). +/ version (StdDdoc) void symlink(RO, RL)(RO original, RL link) -if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) || - isConvertibleToString!RO) && - (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) || - isConvertibleToString!RL)); +if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) && + (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL)); else version (Posix) void symlink(RO, RL)(RO original, RL link) -if ((isInputRange!RO && !isInfinite!RO && isSomeChar!(ElementEncodingType!RO) || - isConvertibleToString!RO) && - (isInputRange!RL && !isInfinite!RL && isSomeChar!(ElementEncodingType!RL) || - isConvertibleToString!RL)) +if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) && + (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL)) { static if (isConvertibleToString!RO || isConvertibleToString!RL) { @@ -3291,11 +3267,9 @@ version (Posix) @safe unittest $(LREF FileException) on error. +/ version (StdDdoc) string readLink(R)(R link) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || - isConvertibleToString!R); +if (isSomeFiniteCharInputRange!R || isConvertibleToString!R); else version (Posix) string readLink(R)(R link) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || - isConvertibleToString!R) +if (isSomeFiniteCharInputRange!R || isConvertibleToString!R) { static if (isConvertibleToString!R) { @@ -3392,7 +3366,7 @@ version (Posix) @system unittest // input range of dchars version (Windows) string getcwd() @trusted { import std.conv : to; - import std.experimental.checkedint : checked; + import std.checkedint : checked; /* GetCurrentDirectory's return value: 1. function succeeds: the number of characters that are written to the buffer, not including the terminating null character. @@ -4216,8 +4190,8 @@ Params: Throws: $(LREF FileException) on error. */ void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault) -if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) && !isConvertibleToString!RF && - isInputRange!RT && !isInfinite!RT && isSomeChar!(ElementEncodingType!RT) && !isConvertibleToString!RT) +if (isSomeFiniteCharInputRange!RF && !isConvertibleToString!RF && + isSomeFiniteCharInputRange!RT && !isConvertibleToString!RT) { // Place outside of @trusted block auto fromz = from.tempCString!FSChar(); @@ -4781,7 +4755,7 @@ private struct DirIteratorImpl } this(R)(R pathname, SpanMode mode, bool followSymlink) - if (isInputRange!R && isSomeChar!(ElementEncodingType!R)) + if (isSomeFiniteCharInputRange!R) { _mode = mode; _followSymlink = followSymlink; diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d index 2d57e489781..6c9e9ae6004 100644 --- a/libphobos/src/std/format/package.d +++ b/libphobos/src/std/format/package.d @@ -179,10 +179,10 @@ $(BOOKTABLE , Depending on the number, a scientific notation or a natural notation is used.)) $(TR $(TD $(B 'a') / $(B 'A')) - $(TD To be formatted as a real number in hexadezimal scientific notation.)) + $(TD To be formatted as a real number in hexadecimal scientific notation.)) $(TR $(TD $(B 'r')) $(TD To be formatted as raw bytes. - The output may not be printable and depends on endianess.)) + The output may not be printable and depends on endianness.)) ) The $(I compound indicator) can be used to describe compound types diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d index b251e4006ec..bc8d368e970 100644 --- a/libphobos/src/std/functional.d +++ b/libphobos/src/std/functional.d @@ -707,19 +707,43 @@ template partial(alias fun, alias arg) { static assert(false, "Cannot apply partial to a non-callable '" ~ fun.stringof ~ "'."); } - else // Assume fun is callable and uniquely defined. + else { - static if (Parameters!fun.length == 0) + import std.meta : Filter; + + static if (__traits(compiles, __traits(getOverloads, + __traits(parent, fun), __traits(identifier, fun)))) + alias overloads = __traits(getOverloads, __traits(parent, fun), + __traits(identifier, fun)); + else + alias overloads = AliasSeq!(fun); + + enum isCallableWithArg(alias fun) = Parameters!fun.length > 0 && + is(typeof(arg) : Parameters!fun[0]); + alias candidates = Filter!(isCallableWithArg, overloads); + + static if (overloads.length == 1 && Parameters!fun.length == 0) { static assert(0, "Cannot partially apply '" ~ fun.stringof ~ "'." ~ "'" ~ fun.stringof ~ "' has 0 arguments."); } - else static if (!is(typeof(arg) : Parameters!fun[0])) + else static if (candidates.length == 0) { + import std.meta : NoDuplicates, staticMap; + + enum hasParameters(alias fun) = Parameters!fun.length > 0; + alias firstParameter(alias fun) = Parameters!fun[0]; + alias firstParameters = NoDuplicates!( + staticMap!(firstParameter, Filter!(hasParameters, overloads))); + string errorMsg() { - string msg = "Argument mismatch for '" ~ fun.stringof ~ "': expected " ~ - Parameters!fun[0].stringof ~ ", but got " ~ typeof(arg).stringof ~ "."; + string msg = "Argument mismatch for '" ~ fun.stringof ~ + "': expected " ~ firstParameters[0].stringof; + static foreach (firstParam; firstParameters[1 .. $]) + msg ~= " or " ~ firstParam.stringof; + msg ~= ", but got " ~ typeof(arg).stringof ~ "."; + return msg; } static assert(0, errorMsg()); @@ -727,10 +751,11 @@ template partial(alias fun, alias arg) else { import std.traits : ReturnType; - ReturnType!fun partial(Parameters!fun[1..$] args2) - { - return fun(arg, args2); - } + static foreach (candidate; candidates) + ReturnType!candidate partial(Parameters!candidate[1..$] args2) + { + return candidate(arg, args2); + } } } } @@ -746,6 +771,22 @@ template partial(alias fun, alias arg) // functions without committing to a particular type of the function. } +// https://issues.dlang.org/show_bug.cgi?id=21457 +/// +@safe unittest +{ + // Overloads are resolved when the partially applied function is called + // with the remaining arguments. + struct S + { + static char fun(int i, string s) { return s[i]; } + static int fun(int a, int b) { return a * b; } + } + alias fun3 = partial!(S.fun, 3); + assert(fun3("hello") == 'l'); + assert(fun3(10) == 30); +} + // tests for partially evaluating callables @safe unittest { diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index ea22d635766..89def0f02f5 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -19,7 +19,7 @@ module std.json; import std.array; import std.conv; -import std.range.primitives; +import std.range; import std.traits; /// @@ -929,7 +929,7 @@ Params: options = enable decoding string representations of NaN/Inf as float values */ JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none) -if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) +if (isSomeFiniteCharInputRange!T) { import std.ascii : isDigit, isHexDigit, toUpper, toLower; import std.typecons : Nullable, Yes; @@ -1437,7 +1437,7 @@ Params: options = enable decoding string representations of NaN/Inf as float values */ JSONValue parseJSON(T)(T json, JSONOptions options) -if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) +if (isSomeFiniteCharInputRange!T) { return parseJSON!T(json, -1, options); } diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d index 2d64684b4a2..20518b86333 100644 --- a/libphobos/src/std/path.d +++ b/libphobos/src/std/path.d @@ -98,7 +98,7 @@ module std.path; import std.file : getcwd; static import std.meta; -import std.range.primitives; +import std.range; import std.traits; version (OSX) @@ -262,8 +262,7 @@ version (Windows) from a path. */ private auto ltrimDirSeparators(R)(R path) -if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementType!R) || - isNarrowString!R) +if (isSomeFiniteCharInputRange!R || isNarrowString!R) { static if (isRandomAccessRange!R && hasSlicing!R || isNarrowString!R) { @@ -3213,12 +3212,8 @@ int filenameCharCmp(CaseSensitive cs = CaseSensitive.osDefault)(dchar a, dchar b */ int filenameCmp(CaseSensitive cs = CaseSensitive.osDefault, Range1, Range2) (Range1 filename1, Range2 filename2) -if (isInputRange!Range1 && !isInfinite!Range1 && - isSomeChar!(ElementEncodingType!Range1) && - !isConvertibleToString!Range1 && - isInputRange!Range2 && !isInfinite!Range2 && - isSomeChar!(ElementEncodingType!Range2) && - !isConvertibleToString!Range2) +if (isSomeFiniteCharInputRange!Range1 && !isConvertibleToString!Range1 && + isSomeFiniteCharInputRange!Range2 && !isConvertibleToString!Range2) { alias C1 = Unqual!(ElementEncodingType!Range1); alias C2 = Unqual!(ElementEncodingType!Range2); diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index 958f606ff52..2c68f36b4e7 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -106,9 +106,8 @@ version (Windows) } import std.internal.cstring; -import std.range.primitives; +import std.range; import std.stdio; -import std.traits : isSomeChar; version (OSX) version = Darwin; @@ -1527,7 +1526,7 @@ package(std) string searchPathFor(scope const(char)[] executable) // current user. version (Posix) private bool isExecutable(R)(R path) @trusted nothrow @nogc -if (isInputRange!R && isSomeChar!(ElementEncodingType!R)) +if (isSomeFiniteCharInputRange!R) { return (access(path.tempCString(), X_OK) == 0); } diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index a21f4d01745..91971342d9a 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -235,7 +235,7 @@ public import std.range.primitives; public import std.typecons : Flag, Yes, No; import std.internal.attributes : betterC; -import std.meta : allSatisfy, staticMap; +import std.meta : allSatisfy, anySatisfy, staticMap; import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral, isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf; @@ -1412,7 +1412,8 @@ auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2) if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) { - return ChooseResult!(R1, R2)(condition, r1, r2); + size_t choice = condition? 0: 1; + return ChooseResult!(R1, R2)(choice, r1, r2); } /// @@ -1447,76 +1448,102 @@ if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && } -private struct ChooseResult(R1, R2) +private struct ChooseResult(Ranges...) { - import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor; + import std.meta : aliasSeqOf, ApplyLeft; + import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor, + lvalueOf; private union { - R1 r1; - R2 r2; + Ranges rs; } - private bool r1Chosen; + private size_t chosenI; - private static auto ref actOnChosen(alias foo, ExtraArgs ...)(ref ChooseResult r, - auto ref ExtraArgs extraArgs) + private static auto ref actOnChosen(alias foo, ExtraArgs ...) + (ref ChooseResult r, auto ref ExtraArgs extraArgs) { - if (r.r1Chosen) - { - ref get1(return ref ChooseResult r) @trusted { return r.r1; } - return foo(get1(r), extraArgs); - } - else + ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } + + switch (r.chosenI) { - ref get2(return ref ChooseResult r) @trusted { return r.r2; } - return foo(get2(r), extraArgs); + static foreach (candI; 0 .. rs.length) + { + case candI: return foo(getI!candI(r), extraArgs); + } + + default: assert(false); } } - this(bool r1Chosen, return scope R1 r1, return scope R2 r2) @trusted + // @trusted because of assignment of r which overlap each other + this(size_t chosen, return scope Ranges rs) @trusted { - // @trusted because of assignment of r1 and r2 which overlap each other import core.lifetime : emplace; - // This should be the only place r1Chosen is ever assigned + // This should be the only place chosenI is ever assigned // independently - this.r1Chosen = r1Chosen; - if (r1Chosen) + this.chosenI = chosen; + + // Otherwise the compiler will complain about skipping these fields + static foreach (i; 0 .. rs.length) { - this.r2 = R2.init; - emplace(&this.r1, r1); + this.rs[i] = Ranges[i].init; } - else + + // The relevant field needs to be initialized last so it will overwrite + // the other initializations and not the other way around. + sw: switch (chosenI) { - this.r1 = R1.init; - emplace(&this.r2, r2); + static foreach (i; 0 .. rs.length) + { + case i: + emplace(&this.rs[i], rs[i]); + break sw; + } + + default: assert(false); } } + // Some legacy code may still call this with typeof(choose(/*...*/))(/*...*/) + // without this overload the regular constructor would invert the meaning of + // the boolean + static if (rs.length == 2) + pragma(inline, true) + deprecated("Call with size_t (0 = first), or use the choose function") + this(bool firstChosen, Ranges rs) + { + import core.lifetime : move; + this(cast(size_t)(firstChosen? 0: 1), rs[0].move, rs[1].move); + } + void opAssign(ChooseResult r) { - static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2) - if (r1Chosen != r.r1Chosen) - { - // destroy the current item - actOnChosen!((ref r) => destroy(r))(this); - } - r1Chosen = r.r1Chosen; - if (r1Chosen) + ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } + + static if (anySatisfy!(hasElaborateDestructor, Ranges)) + if (chosenI != r.chosenI) { - ref get1(return ref ChooseResult r) @trusted { return r.r1; } - get1(this) = get1(r); + // destroy the current item + actOnChosen!((ref r) => destroy(r))(this); } - else + chosenI = r.chosenI; + + sw: switch (chosenI) { - ref get2(return ref ChooseResult r) @trusted { return r.r2; } - get2(this) = get2(r); + static foreach (candI; 0 .. rs.length) + { + case candI: getI!candI(this) = getI!candI(r); + break sw; + } + + default: assert(false); } } // Carefully defined postblit to postblit the appropriate range - static if (hasElaborateCopyConstructor!R1 - || hasElaborateCopyConstructor!R2) + static if (anySatisfy!(hasElaborateCopyConstructor, Ranges)) this(this) { actOnChosen!((ref r) { @@ -1524,20 +1551,18 @@ private struct ChooseResult(R1, R2) })(this); } - static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2) + static if (anySatisfy!(hasElaborateDestructor, Ranges)) ~this() { actOnChosen!((ref r) => destroy(r))(this); } - static if (isInfinite!R1 && isInfinite!R2) - // Propagate infiniteness. - enum bool empty = false; - else - @property bool empty() - { - return actOnChosen!(r => r.empty)(this); - } + // Propagate infiniteness. + static if (allSatisfy!(isInfinite, Ranges)) enum bool empty = false; + else @property bool empty() + { + return actOnChosen!(r => r.empty)(this); + } @property auto ref front() { @@ -1550,34 +1575,38 @@ private struct ChooseResult(R1, R2) return actOnChosen!((ref r) { r.popFront; })(this); } - static if (isForwardRange!R1 && isForwardRange!R2) - @property auto save() return scope + static if (allSatisfy!(isForwardRange, Ranges)) + @property auto save() // return scope inferred { - if (r1Chosen) + auto saveOrInit(size_t i)() { - ref R1 getR1() @trusted { return r1; } - return ChooseResult(r1Chosen, getR1.save, R2.init); - } - else - { - ref R2 getR2() @trusted { return r2; } - return ChooseResult(r1Chosen, R1.init, getR2.save); + ref getI() @trusted { return rs[i]; } + if (i == chosenI) return getI().save; + else return Ranges[i].init; } + + return typeof(this)(chosenI, staticMap!(saveOrInit, + aliasSeqOf!(rs.length.iota))); } - @property void front(T)(T v) - if (is(typeof({ r1.front = v; r2.front = v; }))) + template front(T) { - actOnChosen!((ref r, T v) { r.front = v; })(this, v); - } + private enum overloadValidFor(alias r) = is(typeof(r.front = T.init)); - static if (hasMobileElements!R1 && hasMobileElements!R2) - auto moveFront() + static if (allSatisfy!(overloadValidFor, rs)) + void front(T v) { - return actOnChosen!((ref r) => r.moveFront)(this); + actOnChosen!((ref r, T v) { r.front = v; })(this, v); } + } - static if (isBidirectionalRange!R1 && isBidirectionalRange!R2) + static if (allSatisfy!(hasMobileElements, Ranges)) + auto moveFront() + { + return actOnChosen!((ref r) => r.moveFront)(this); + } + + static if (allSatisfy!(isBidirectionalRange, Ranges)) { @property auto ref back() { @@ -1590,20 +1619,25 @@ private struct ChooseResult(R1, R2) actOnChosen!((ref r) { r.popBack; })(this); } - static if (hasMobileElements!R1 && hasMobileElements!R2) - auto moveBack() - { - return actOnChosen!((ref r) => r.moveBack)(this); - } + static if (allSatisfy!(hasMobileElements, Ranges)) + auto moveBack() + { + return actOnChosen!((ref r) => r.moveBack)(this); + } - @property void back(T)(T v) - if (is(typeof({ r1.back = v; r2.back = v; }))) + template back(T) { - actOnChosen!((ref r, T v) { r.back = v; })(this, v); + private enum overloadValidFor(alias r) = is(typeof(r.back = T.init)); + + static if (allSatisfy!(overloadValidFor, rs)) + void back(T v) + { + actOnChosen!((ref r, T v) { r.back = v; })(this, v); + } } } - static if (hasLength!R1 && hasLength!R2) + static if (allSatisfy!(hasLength, Ranges)) { @property size_t length() { @@ -1612,7 +1646,7 @@ private struct ChooseResult(R1, R2) alias opDollar = length; } - static if (isRandomAccessRange!R1 && isRandomAccessRange!R2) + static if (allSatisfy!(isRandomAccessRange, Ranges)) { auto ref opIndex(size_t index) { @@ -1620,33 +1654,41 @@ private struct ChooseResult(R1, R2) return actOnChosen!get(this, index); } - static if (hasMobileElements!R1 && hasMobileElements!R2) + static if (allSatisfy!(hasMobileElements, Ranges)) auto moveAt(size_t index) { return actOnChosen!((ref r, size_t index) => r.moveAt(index)) (this, index); } - void opIndexAssign(T)(T v, size_t index) - if (is(typeof({ r1[1] = v; r2[1] = v; }))) + private enum indexAssignable(T, R) = is(typeof(lvalueOf!R[1] = T.init)); + + template opIndexAssign(T) + if (allSatisfy!(ApplyLeft!(indexAssignable, T), Ranges)) { - return actOnChosen!((ref r, size_t index, T v) { r[index] = v; }) - (this, index, v); + void opIndexAssign(T v, size_t index) + { + return actOnChosen!((ref r, size_t index, T v) { r[index] = v; }) + (this, index, v); + } } } - static if (hasSlicing!R1 && hasSlicing!R2) - auto opSlice(size_t begin, size_t end) + static if (allSatisfy!(hasSlicing, Ranges)) + auto opSlice(size_t begin, size_t end) + { + alias Slice(R) = typeof(R.init[0 .. 1]); + alias Slices = staticMap!(Slice, Ranges); + + auto sliceOrInit(size_t i)() { - alias Slice1 = typeof(R1.init[0 .. 1]); - alias Slice2 = typeof(R2.init[0 .. 1]); - return actOnChosen!((r, size_t begin, size_t end) { - static if (is(typeof(r) == Slice1)) - return choose(true, r[begin .. end], Slice2.init); - else - return choose(false, Slice1.init, r[begin .. end]); - })(this, begin, end); + ref getI() @trusted { return rs[i]; } + return i == chosenI? getI()[begin .. end]: Slices[i].init; } + + return chooseAmong(chosenI, staticMap!(sliceOrInit, + aliasSeqOf!(rs.length.iota))); + } } // https://issues.dlang.org/show_bug.cgi?id=18657 @@ -1668,8 +1710,9 @@ pure @safe unittest int front; bool empty; void popFront() {} - @property R save() { p = q; return this; } - // `p = q;` is only there to prevent inference of `scope return`. + // `p = q;` is only there to prevent inference of `scope return`. + @property @safe R save() { p = q; return this; } + } R r; choose(true, r, r).save; @@ -1801,10 +1844,7 @@ if (Ranges.length >= 2 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && !is(CommonType!(staticMap!(ElementType, Ranges)) == void)) { - static if (Ranges.length == 2) - return choose(index == 0, rs[0], rs[1]); - else - return choose(index == 0, rs[0], chooseAmong(index - 1, rs[1 .. $])); + return ChooseResult!Ranges(index, rs); } /// @@ -13521,3 +13561,49 @@ pure @safe unittest assert([1, 2, 3, 4].padRight(0, 10)[7 .. 9].equal([0, 0])); } + +/** +This simplifies a commonly used idiom in phobos for accepting any kind of string +parameter. The type `R` can for example be a simple string, chained string using +$(REF chain, std,range), $(REF chainPath, std,path) or any other input range of +characters. + +Only finite length character ranges are allowed with this constraint. + +This template is equivalent to: +--- +isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) +--- + +See_Also: +$(REF isInputRange, std,range,primitives), +$(REF isInfinite, std,range,primitives), +$(LREF isSomeChar), +$(REF ElementEncodingType, std,range,primitives) +*/ +template isSomeFiniteCharInputRange(R) +{ + import std.traits : isSomeChar; + + enum isSomeFiniteCharInputRange = isInputRange!R && !isInfinite!R + && isSomeChar!(ElementEncodingType!R); +} + +/// +@safe unittest +{ + import std.path : chainPath; + import std.range : chain; + + void someLibraryMethod(R)(R argument) + if (isSomeFiniteCharInputRange!R) + { + // implementation detail, would iterate over each character of argument + } + + someLibraryMethod("simple strings work"); + someLibraryMethod(chain("chained", " ", "strings", " ", "work")); + someLibraryMethod(chainPath("chained", "paths", "work")); + // you can also use custom structs implementing a char range +} + diff --git a/libphobos/src/std/regex/package.d b/libphobos/src/std/regex/package.d index 8db0b1e1f59..e24abc50a07 100644 --- a/libphobos/src/std/regex/package.d +++ b/libphobos/src/std/regex/package.d @@ -170,6 +170,10 @@ $(TR $(TD Objects) $(TD Greedy version - tries as many times as possible.) $(REG_ROW +?, Matches previous character/subexpression 1 or more times. Lazy version - stops as early as possible.) + $(REG_ROW ?, Matches previous character/subexpression 0 or 1 time. + Greedy version - tries as many times as possible.) + $(REG_ROW ??, Matches previous character/subexpression 0 or 1 time. + Lazy version - stops as early as possible.) $(REG_ROW {n}, Matches previous character/subexpression exactly n times. ) $(REG_ROW {n$(COMMA)}, Matches previous character/subexpression n times or more. Greedy version - tries as many times as possible. ) @@ -1745,3 +1749,9 @@ auto escaper(Range)(Range r) assert(s2.escaper.equal("")); }} } + +@system unittest +{ + assert("ab".matchFirst(regex(`a?b?`)).hit == "ab"); + assert("ab".matchFirst(regex(`a??b?`)).hit == ""); +} diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index f30ea80ae44..bc2d3fe4210 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -50,8 +50,8 @@ import core.stdc.stddef : wchar_t; public import core.stdc.stdio; import std.algorithm.mutation : copy; import std.meta : allSatisfy; -import std.range.primitives : ElementEncodingType, empty, front, - isBidirectionalRange, isInputRange, put; +import std.range : ElementEncodingType, empty, front, isBidirectionalRange, + isInputRange, isSomeFiniteCharInputRange, put; import std.traits : isSomeChar, isSomeString, Unqual, isPointer; import std.typecons : Flag, No, Yes; @@ -555,7 +555,7 @@ Throws: `ErrnoException` if the file could not be opened. /// ditto this(R1, R2)(R1 name) - if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1)) + if (isSomeFiniteCharInputRange!R1) { import std.conv : to; this(name.to!string, "rb"); @@ -563,8 +563,8 @@ Throws: `ErrnoException` if the file could not be opened. /// ditto this(R1, R2)(R1 name, R2 mode) - if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) && - isInputRange!R2 && isSomeChar!(ElementEncodingType!R2)) + if (isSomeFiniteCharInputRange!R1 && + isSomeFiniteCharInputRange!R2) { import std.conv : to; this(name.to!string, mode.to!string); @@ -957,7 +957,7 @@ Throws: `Exception` if the file is not opened. } /** -If the file is not opened, returns `true`. Otherwise, returns +If the file is closed or not yet opened, returns `true`. Otherwise, returns $(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for the file handle. */ @@ -1013,8 +1013,8 @@ Throws: `ErrnoException` on failure if closing the file. } /** -If the file was unopened, succeeds vacuously. Otherwise closes the -file (by calling $(HTTP +If the file was closed or not yet opened, succeeds vacuously. Otherwise +closes the file (by calling $(HTTP cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)), throwing on error. Even if an exception is thrown, afterwards the $(D File) object is empty. This is different from `detach` in that it @@ -1042,7 +1042,7 @@ Throws: `ErrnoException` on error. } /** -If the file is not opened, succeeds vacuously. Otherwise, returns +If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns $(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html, _clearerr) for the file handle. */ @@ -4642,8 +4642,8 @@ if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) && * with appropriately-constructed C-style strings. */ private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r") -if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) && - (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2)) +if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) && + (isSomeFiniteCharInputRange!R2 || isSomeString!R2)) { import std.internal.cstring : tempCString; @@ -4684,8 +4684,8 @@ version (Posix) * with appropriately-constructed C-style strings. */ FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc - if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) && - (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2)) + if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) && + (isSomeFiniteCharInputRange!R2 || isSomeString!R2)) { import std.internal.cstring : tempCString; diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index c1d6bc97402..596c11cfb00 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -5195,16 +5195,54 @@ enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAss /** Returns `true` iff an rvalue of type `Rhs` can be assigned to a variable of -type `Lhs` +type `Lhs`. */ enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; }); +/// +@safe unittest +{ + struct S1 + { + void opAssign(S1); + } + + struct S2 + { + void opAssign(ref S2); + } + + static assert( isRvalueAssignable!(long, int)); + static assert(!isRvalueAssignable!(int, long)); + static assert( isRvalueAssignable!S1); + static assert(!isRvalueAssignable!S2); +} + /** Returns `true` iff an lvalue of type `Rhs` can be assigned to a variable of -type `Lhs` +type `Lhs`. */ enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; }); +/// +@safe unittest +{ + struct S1 + { + void opAssign(S1); + } + + struct S2 + { + void opAssign(ref S2); + } + + static assert( isLvalueAssignable!(long, int)); + static assert(!isLvalueAssignable!(int, long)); + static assert( isLvalueAssignable!S1); + static assert( isLvalueAssignable!S2); +} + @safe unittest { static assert(!isAssignable!(immutable int, int)); @@ -6095,7 +6133,7 @@ template BuiltinTypeOf(T) alias X = OriginalType!T; static if (__traits(isArithmetic, X) && !is(X == __vector) || __traits(isStaticArray, X) || is(X == E[], E) || - __traits(isAssociativeArray, X)) + __traits(isAssociativeArray, X) || is(X == typeof(null))) alias BuiltinTypeOf = X; else static assert(0); @@ -6117,7 +6155,13 @@ enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool); static assert( isBoolean!bool); enum EB : bool { a = true } static assert( isBoolean!EB); - static assert(!isBoolean!(SubTypeOf!bool)); + + struct SubTypeOfBool + { + bool val; + alias val this; + } + static assert(!isBoolean!(SubTypeOfBool)); } @safe unittest @@ -6994,6 +7038,18 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; */ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); +/// +@safe unittest +{ + struct S; + + static assert( isAssociativeArray!(int[string])); + static assert( isAssociativeArray!(S[S])); + static assert(!isAssociativeArray!(string[])); + static assert(!isAssociativeArray!S); + static assert(!isAssociativeArray!(int[4])); +} + @safe unittest { struct Foo @@ -7037,6 +7093,7 @@ enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; static assert( isBuiltinType!string); static assert( isBuiltinType!(int[])); static assert( isBuiltinType!(C[string])); + static assert( isBuiltinType!(typeof(null))); static assert(!isBuiltinType!C); static assert(!isBuiltinType!U); static assert(!isBuiltinType!S); @@ -7049,6 +7106,7 @@ enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; */ enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); +/// @safe unittest { static if (is(__vector(float[4]))) @@ -7066,6 +7124,20 @@ enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); */ enum bool isPointer(T) = is(T == U*, U) && __traits(isScalar, T); +/// +@safe unittest +{ + void fun(); + + static assert( isPointer!(int*)); + static assert( isPointer!(int function())); + static assert(!isPointer!int); + static assert(!isPointer!string); + static assert(!isPointer!(typeof(null))); + static assert(!isPointer!(typeof(fun))); + static assert(!isPointer!(int delegate())); +} + @safe unittest { static foreach (T; AliasSeq!(int*, void*, char[]*)) @@ -8835,6 +8907,27 @@ enum bool allSameType(Ts...) = */ enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init)) {} }); +/// +@safe unittest +{ + class C; + struct S1; + struct S2 + { + T opCast(T)() const; + } + + static assert( ifTestable!bool); + static assert( ifTestable!int); + static assert( ifTestable!(S1*)); + static assert( ifTestable!(typeof(null))); + static assert( ifTestable!(int[])); + static assert( ifTestable!(int[string])); + static assert( ifTestable!S2); + static assert( ifTestable!C); + static assert(!ifTestable!S1); +} + @safe unittest { import std.meta : AliasSeq, allSatisfy; diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index cde2b9da055..28edb9b8c29 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -76,36 +76,42 @@ import std.range.primitives : isOutputRange; import std.traits; import std.internal.attributes : betterC; -/// +/// Value tuples @safe unittest { - // value tuples alias Coord = Tuple!(int, "x", int, "y", int, "z"); Coord c; c[1] = 1; // access by index c.z = 1; // access by given name assert(c == Coord(0, 1, 1)); - // names can be omitted - alias DicEntry = Tuple!(string, string); + // names can be omitted, types can be mixed + alias DictEntry = Tuple!(string, int); + auto dict = DictEntry("seven", 7); - // tuples can also be constructed on instantiation + // element types can be inferred assert(tuple(2, 3, 4)[1] == 3); - // construction on instantiation works with names too - assert(tuple!("x", "y", "z")(2, 3, 4).y == 3); + // type inference works with names too + auto tup = tuple!("x", "y", "z")(2, 3, 4); + assert(tup.y == 3); +} - // Rebindable references to const and immutable objects +/// Rebindable references to const and immutable objects +@safe unittest +{ + class Widget { - class Widget { void foo() const @safe {} } - const w1 = new Widget, w2 = new Widget; - w1.foo(); - // w1 = w2 would not work; can't rebind const object - auto r = Rebindable!(const Widget)(w1); - // invoke method as if r were a Widget object - r.foo(); - // rebind r to refer to another object - r = w2; + void foo() const @safe {} } + const w1 = new Widget, w2 = new Widget; + w1.foo(); + // w1 = w2 would not work; can't rebind const object + + auto r = Rebindable!(const Widget)(w1); + // invoke method as if r were a Widget object + r.foo(); + // rebind r to refer to another object + r = w2; } /** @@ -4036,7 +4042,7 @@ template apply(alias fun) import std.functional : unaryFun; auto apply(T)(auto ref T t) - if (isInstanceOf!(Nullable, T) && is(typeof(unaryFun!fun(T.init.get)))) + if (isInstanceOf!(Nullable, T)) { alias FunType = typeof(unaryFun!fun(T.init.get)); @@ -5107,6 +5113,46 @@ private static: static abstract class C_9 : K {} auto o = new BlackHole!C_9; }+/ + // test `parent` alias + { + interface I_11 + { + void simple(int) @safe; + int anotherSimple(string); + int overloaded(int); + /+ XXX [BUG 19715] + void overloaded(string) @safe; + +/ + } + + static class C_11 + { + import std.traits : Parameters, ReturnType; + import std.meta : Alias; + + protected ReturnType!fn _impl(alias fn)(Parameters!fn) + if (is(Alias!(__traits(parent, fn)) == interface)) + { + static if (!is(typeof(return) == void)) + return typeof(return).init; + } + } + + template tpl(I, alias fn) + if (is(I == interface) && __traits(isSame, __traits(parent, fn), I)) + { + enum string tpl = q{ + enum bool haveReturn = !is(typeof(return) == void); + + static if (is(typeof(return) == void)) + _impl!parent(args); + else + return _impl!parent(args); + }; + } + + auto o = new AutoImplement!(I_11, C_11, tpl); + } } // https://issues.dlang.org/show_bug.cgi?id=17177 @@ -5188,6 +5234,7 @@ version (StdUnittest) void bar(int a) { } } } + @system unittest { auto foo = new issue10647_DoNothing!issue10647_Foo(); @@ -5424,8 +5471,8 @@ private static: if (!isCtor) { preamble ~= "alias self = " ~ name ~ ";\n"; - if (WITH_BASE_CLASS && !__traits(isAbstractFunction, func)) - preamble ~= `alias parent = __traits(getMember, super, "` ~ name ~ `");`; + static if (WITH_BASE_CLASS) + preamble ~= `alias parent = __traits(getMember, ` ~ Policy.BASE_CLASS_ID ~ `, "` ~ name ~ `");`; } // Function body diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d index a29025a179a..5c23684b9ae 100644 --- a/libphobos/src/std/utf.d +++ b/libphobos/src/std/utf.d @@ -65,9 +65,9 @@ module std.utf; import std.exception : basicExceptionCtors; import core.exception : UnicodeException; import std.meta : AliasSeq; -import std.range.primitives; -import std.traits : isAutodecodableString, isPointer, isSomeChar, - isSomeString, isStaticArray, Unqual, isConvertibleToString; +import std.range; +import std.traits : isAutodecodableString, isConvertibleToString, isPointer, + isSomeChar, isSomeString, isStaticArray, Unqual; import std.typecons : Flag, Yes, No; @@ -2809,7 +2809,7 @@ if (isSomeChar!C) The number of code units in `input` when encoded to `C` +/ size_t codeLength(C, InputRange)(InputRange input) -if (isInputRange!InputRange && !isInfinite!InputRange && isSomeChar!(ElementType!InputRange)) +if (isSomeFiniteCharInputRange!InputRange) { alias EncType = Unqual!(ElementEncodingType!InputRange); static if (isSomeString!InputRange && is(EncType == C) && is(typeof(input.length))) @@ -2961,7 +2961,7 @@ if (isSomeString!S) * For a lazy, non-allocating version of these functions, see $(LREF byUTF). */ string toUTF8(S)(S s) -if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) +if (isSomeFiniteCharInputRange!S) { return toUTFImpl!string(s); } @@ -3003,7 +3003,7 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) * For a lazy, non-allocating version of these functions, see $(LREF byUTF). */ wstring toUTF16(S)(S s) -if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) +if (isSomeFiniteCharInputRange!S) { return toUTFImpl!wstring(s); } @@ -3047,7 +3047,7 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) * For a lazy, non-allocating version of these functions, see $(LREF byUTF). */ dstring toUTF32(S)(scope S s) -if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) +if (isSomeFiniteCharInputRange!S) { return toUTFImpl!dstring(s); } diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d index ce635fb9b34..41cd4848b12 100644 --- a/libphobos/src/std/variant.d +++ b/libphobos/src/std/variant.d @@ -472,6 +472,20 @@ private: auto rhsPA = getPtr(&temp.store); return compare(rhsPA, zis, selector); } + // Generate the function below only if the Variant's type is + // comparable with 'null' + static if (__traits(compiles, () => A.init == null)) + { + if (rhsType == typeid(null)) + { + // if rhsType is typeof(null), then we're comparing with 'null' + // this takes into account 'opEquals' and 'opCmp' + // all types that can compare with null have to following properties: + // if it's 'null' then it's equal to null, otherwise it's always greater + // than 'null' + return *zis == null ? 0 : 1; + } + } return ptrdiff_t.min; // dunno case OpID.toString: auto target = cast(string*) parm; @@ -1608,6 +1622,42 @@ pure nothrow @nogc assert(v != b); } +// https://issues.dlang.org/show_bug.cgi?id=22647 +// Can compare with 'null' +@system unittest +{ + static struct Bar + { + int* ptr; + alias ptr this; + } + + static class Foo {} + int* iptr; + int[] arr; + + Variant v = Foo.init; // 'null' + assert(v != null); // can only compare objects with 'null' by using 'is' + + v = iptr; + assert(v == null); // pointers can be compared with 'null' + + v = arr; + assert(v == null); // arrays can be compared with 'null' + + v = ""; + assert(v == null); // strings are arrays, an empty string is considered 'null' + + v = Bar.init; + assert(v == null); // works with alias this + + v = [3]; + assert(v != null); + assert(v > null); + assert(v >= null); + assert(!(v < null)); +} + /** _Algebraic data type restricted to a closed set of possible types. It's an alias for $(LREF VariantN) with an @@ -2052,10 +2102,10 @@ static class VariantException : Exception assert(v == 2); assert(v < 3); - static assert(!__traits(compiles, {v == long.max;})); - static assert(!__traits(compiles, {v == null;})); - static assert(!__traits(compiles, {v < long.max;})); - static assert(!__traits(compiles, {v > null;})); + static assert(!__traits(compiles, () => v == long.max)); + static assert(!__traits(compiles, () => v == null)); + static assert(!__traits(compiles, () => v < long.max)); + static assert(!__traits(compiles, () => v > null)); } // https://issues.dlang.org/show_bug.cgi?id=1558 diff --git a/libphobos/testsuite/testsuite_flags.in b/libphobos/testsuite/testsuite_flags.in index 42053b21163..528cff4bf13 100755 --- a/libphobos/testsuite/testsuite_flags.in +++ b/libphobos/testsuite/testsuite_flags.in @@ -29,7 +29,9 @@ case ${query} in --gdcflags) GDCFLAGS_default="-fmessage-length=0 -fno-show-column" GDCFLAGS_config="@WARN_DFLAGS@ @GDCFLAGS@ @CET_DFLAGS@ - @phobos_compiler_shared_flag@ -fpreview=dip1000 -fno-release -funittest" + @phobos_compiler_shared_flag@ + -fall-instantiations -fpreview=dip1000 + -fno-release -funittest" echo ${GDCFLAGS_default} ${GDCFLAGS_config} ;; --gdcpaths) |