diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-07 01:05:54 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-11 02:21:39 +0100 |
commit | dd3026f05111a0858ee87146ba9c37f164afa815 (patch) | |
tree | 98325195cedbde6e098d1994ae97975e1461ae21 /libphobos/src/std | |
parent | b0eeb540497c7b9dee01f8724f9a4978b53a12ae (diff) | |
download | gcc-dd3026f05111a0858ee87146ba9c37f164afa815.zip gcc-dd3026f05111a0858ee87146ba9c37f164afa815.tar.gz gcc-dd3026f05111a0858ee87146ba9c37f164afa815.tar.bz2 |
d: Merge dmd, druntime 2b89c2909d, phobos bdedad3bf
D front-end changes:
- Import latest fixes from dmd v2.110.0-beta.1.
D runtime changes:
- Import latest fixes from druntime v2.110.0-beta.1.
Phobos changes:
- Import latest fixes from phobos v2.110.0-beta.1.
- Added `popGrapheme' function to `std.uni'.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd 2b89c2909d.
* Make-lang.in (D_FRONTEND_OBJS): Rename d/basicmangle.o to
d/mangle-basic.o, d/cppmangle.o to d/mangle-cpp.o, and d/dmangle.o to
d/mangle-package.o.
(d/mangle-%.o): New rule.
* d-builtins.cc (maybe_set_builtin_1): Update for new front-end
interface.
* d-diagnostic.cc (verrorReport): Likewise.
(verrorReportSupplemental): Likewise.
* d-frontend.cc (getTypeInfoType): Likewise.
* d-lang.cc (d_init_options): Likewise.
(d_handle_option): Likewise.
(d_post_options): Likewise.
* d-target.cc (TargetC::contributesToAggregateAlignment): New.
* d-tree.h (create_typeinfo): Adjust prototype.
* decl.cc (layout_struct_initializer): Update for new front-end
interface.
* typeinfo.cc (create_typeinfo): Remove generate parameter.
* types.cc (layout_aggregate_members): Update for new front-end
interface.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime 2b89c2909d.
* src/MERGE: Merge upstream phobos bdedad3bf.
Diffstat (limited to 'libphobos/src/std')
52 files changed, 1482 insertions, 823 deletions
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d index 5c70960..60fd114 100644 --- a/libphobos/src/std/algorithm/comparison.d +++ b/libphobos/src/std/algorithm/comparison.d @@ -102,7 +102,7 @@ template among(values...) if (isExpressionTuple!values) { uint among(Value)(Value value) - if (!is(CommonType!(Value, values) == void)) + if (!is(CommonType!(Value, values) == void)) { switch (value) { diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index 1453d2b..8a3add3 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -443,7 +443,8 @@ if (fun.length >= 1) A range with each fun applied to all the elements. If there is more than one fun, the element type will be `Tuple` containing one element for each fun. */ - auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) + auto map(Range)(Range r) + if (isInputRange!(Unqual!Range)) { import std.meta : AliasSeq, staticMap; @@ -1308,7 +1309,8 @@ if (is(typeof(unaryFun!predicate))) A range containing only elements `x` in `range` for which `predicate(x)` returns `true`. */ - auto filter(Range)(Range range) if (isInputRange!(Unqual!Range)) + auto filter(Range)(Range range) + if (isInputRange!(Unqual!Range)) { return FilterResult!(unaryFun!predicate, Range)(range); } @@ -1545,7 +1547,8 @@ template filterBidirectional(alias pred) Returns: A range containing only the elements in `r` for which `pred` returns `true`. */ - auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range)) + auto filterBidirectional(Range)(Range r) + if (isBidirectionalRange!(Unqual!Range)) { return FilterBidiResult!(unaryFun!pred, Range)(r); } diff --git a/libphobos/src/std/algorithm/mutation.d b/libphobos/src/std/algorithm/mutation.d index e434d24..ea1a1b2 100644 --- a/libphobos/src/std/algorithm/mutation.d +++ b/libphobos/src/std/algorithm/mutation.d @@ -2860,6 +2860,13 @@ Params: lhs = Data to be swapped with `rhs`. rhs = Data to be swapped with `lhs`. */ +void swap(T)(ref T lhs, ref T rhs) +if (is(typeof(lhs.proxySwap(rhs)))) +{ + lhs.proxySwap(rhs); +} + +/// ditto void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) { @@ -3121,13 +3128,6 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) swap(a3, a4); } -/// ditto -void swap(T)(ref T lhs, ref T rhs) -if (is(typeof(lhs.proxySwap(rhs)))) -{ - lhs.proxySwap(rhs); -} - /** Swaps two elements in-place of a range `r`, specified by their indices `i1` and `i2`. diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d index cb47153..29839d1 100644 --- a/libphobos/src/std/algorithm/sorting.d +++ b/libphobos/src/std/algorithm/sorting.d @@ -2625,11 +2625,21 @@ private template TimSortImpl(alias pred, R) // can't use `temp.length` if there's no default constructor static if (__traits(compiles, { T defaultConstructed; cast(void) defaultConstructed; })) { - if (__ctfe) temp.length = newSize; - else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); + + static if (hasElaborateAssign!T) + temp.length = newSize; + else + { + if (__ctfe) temp.length = newSize; + else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); + } } else { + static assert(!hasElaborateAssign!T, + "Structs which have opAssign but cannot be default-initialized " ~ + "do not currently work with stable sort: " ~ + "https://issues.dlang.org/show_bug.cgi?id=24810"); temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); } } @@ -3093,6 +3103,65 @@ private template TimSortImpl(alias pred, R) array.sort!((a, b) => false, SwapStrategy.stable); } +// https://issues.dlang.org/show_bug.cgi?id=24809 +@safe unittest +{ + static struct E + { + int value; + int valid = 42; + + ~this() + { + assert(valid == 42); + } + } + + import std.array : array; + import std.range : chain, only, repeat; + auto arr = chain(repeat(E(41), 18), + only(E(39)), + repeat(E(41), 16), + only(E(1)), + repeat(E(42), 33), + only(E(33)), + repeat(E(42), 16), + repeat(E(43), 27), + only(E(33)), + repeat(E(43), 34), + only(E(34)), + only(E(43)), + only(E(63)), + repeat(E(44), 42), + only(E(27)), + repeat(E(44), 11), + repeat(E(45), 64), + repeat(E(46), 3), + only(E(11)), + repeat(E(46), 7), + only(E(4)), + repeat(E(46), 34), + only(E(36)), + repeat(E(46), 17), + repeat(E(47), 36), + only(E(39)), + repeat(E(47), 26), + repeat(E(48), 17), + only(E(21)), + repeat(E(48), 5), + only(E(39)), + repeat(E(48), 14), + only(E(58)), + repeat(E(48), 24), + repeat(E(49), 13), + only(E(40)), + repeat(E(49), 38), + only(E(18)), + repeat(E(49), 11), + repeat(E(50), 6)).array(); + + arr.sort!((a, b) => a.value < b.value, SwapStrategy.stable)(); +} // schwartzSort /** diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index 27d2167..acd5311 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -3701,7 +3701,8 @@ if (isDynamicArray!A) * Params: * item = the single item to append */ - void put(U)(U item) if (canPutItem!U) + void put(U)(U item) + if (canPutItem!U) { static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof) { @@ -3730,7 +3731,8 @@ if (isDynamicArray!A) } // Const fixing hack. - void put(Range)(Range items) if (canPutConstRange!Range) + void put(Range)(Range items) + if (canPutConstRange!Range) { alias p = put!(Unqual!Range); p(items); @@ -3743,7 +3745,8 @@ if (isDynamicArray!A) * Params: * items = the range of items to append */ - void put(Range)(Range items) if (canPutRange!Range) + void put(Range)(Range items) + if (canPutRange!Range) { // note, we disable this branch for appending one type of char to // another because we can't trust the length portion. diff --git a/libphobos/src/std/base64.d b/libphobos/src/std/base64.d index 0fc92ac..0ce81b5 100644 --- a/libphobos/src/std/base64.d +++ b/libphobos/src/std/base64.d @@ -299,9 +299,10 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - char[] encode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : ubyte) && hasLength!R1 && - is(R2 == char[])) + char[] encode(R1, R2)(R1 source, R2 buffer) + if (!isArray!R1 && isInputRange!R1 && + is(ElementType!R1 : ubyte) && hasLength!R1 && + is(R2 == char[])) in { assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding"); @@ -474,8 +475,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * ditto */ size_t encode(R1, R2)(R1 source, auto ref R2 range) - if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) && - hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char)) + if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) && + hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char)) { immutable srcLen = source.length; if (srcLen == 0) @@ -559,7 +560,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * A newly-allocated `char[]` buffer containing the encoded string. */ @safe - pure char[] encode(Range)(Range source) if (isArray!Range && is(ElementType!Range : ubyte)) + pure char[] encode(Range)(Range source) + if (isArray!Range && is(ElementType!Range : ubyte)) { return encode(source, new char[encodeLength(source.length)]); } @@ -575,8 +577,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - char[] encode(Range)(Range source) if (!isArray!Range && isInputRange!Range && - is(ElementType!Range : ubyte) && hasLength!Range) + char[] encode(Range)(Range source) + if (!isArray!Range && isInputRange!Range && + is(ElementType!Range : ubyte) && hasLength!Range) { return encode(source, new char[encodeLength(source.length)]); } @@ -592,8 +595,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF encoder) function instead. */ - struct Encoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) || - is(ElementType!Range : const(char)[]))) + struct Encoder(Range) + if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) || + is(ElementType!Range : const(char)[]))) { private: Range range_; @@ -702,7 +706,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF encoder) function instead. */ - struct Encoder(Range) if (isInputRange!Range && is(ElementType!Range : ubyte)) + struct Encoder(Range) + if (isInputRange!Range && is(ElementType!Range : ubyte)) { private: Range range_; @@ -884,7 +889,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * } * ----- */ - Encoder!(Range) encoder(Range)(Range range) if (isInputRange!Range) + Encoder!(Range) encoder(Range)(Range range) + if (isInputRange!Range) { return typeof(return)(range); } @@ -981,8 +987,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * base alphabet of the current Base64 encoding scheme. */ @trusted - pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) && - is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer) + if (isArray!R1 && is(ElementType!R1 : dchar) && + is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) in { assert(buffer.length >= realDecodeLength(source), "Insufficient buffer for decoding"); @@ -1065,9 +1072,10 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - ubyte[] decode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 && - is(ElementType!R1 : dchar) && hasLength!R1 && - is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + ubyte[] decode(R1, R2)(R1 source, R2 buffer) + if (!isArray!R1 && isInputRange!R1 && + is(ElementType!R1 : dchar) && hasLength!R1 && + is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) in { assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding"); @@ -1156,8 +1164,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * base alphabet of the current Base64 encoding scheme. */ size_t decode(R1, R2)(in R1 source, auto ref R2 range) - if (isArray!R1 && is(ElementType!R1 : dchar) && - !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + if (isArray!R1 && is(ElementType!R1 : dchar) && + !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) out(result) { immutable expect = realDecodeLength(source); @@ -1244,8 +1252,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * ditto */ size_t decode(R1, R2)(R1 source, auto ref R2 range) - if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) && - hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) + if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) && + hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte)) out(result) { // @@@BUG@@@ Workaround for DbC problem. @@ -1334,7 +1342,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * A newly-allocated `ubyte[]` buffer containing the decoded string. */ @safe - pure ubyte[] decode(Range)(Range source) if (isArray!Range && is(ElementType!Range : dchar)) + pure ubyte[] decode(Range)(Range source) + if (isArray!Range && is(ElementType!Range : dchar)) { return decode(source, new ubyte[decodeLength(source.length)]); } @@ -1350,8 +1359,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') /** * ditto */ - ubyte[] decode(Range)(Range source) if (!isArray!Range && isInputRange!Range && - is(ElementType!Range : dchar) && hasLength!Range) + ubyte[] decode(Range)(Range source) + if (!isArray!Range && isInputRange!Range && + is(ElementType!Range : dchar) && hasLength!Range) { return decode(source, new ubyte[decodeLength(source.length)]); } @@ -1367,8 +1377,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF decoder) function instead. */ - struct Decoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(char)[]) || - is(ElementType!Range : const(ubyte)[]))) + struct Decoder(Range) + if (isInputRange!Range && (is(ElementType!Range : const(char)[]) || + is(ElementType!Range : const(ubyte)[]))) { private: Range range_; @@ -1492,7 +1503,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * Note: This struct is not intended to be created in user code directly; * use the $(LREF decoder) function instead. */ - struct Decoder(Range) if (isInputRange!Range && is(ElementType!Range : char)) + struct Decoder(Range) + if (isInputRange!Range && is(ElementType!Range : char)) { private: Range range_; @@ -1683,7 +1695,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=') * } * ----- */ - Decoder!(Range) decoder(Range)(Range range) if (isInputRange!Range) + Decoder!(Range) decoder(Range)(Range range) + if (isInputRange!Range) { return typeof(return)(range); } diff --git a/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d index 0240ea1..a8b3897 100644 --- a/libphobos/src/std/bigint.d +++ b/libphobos/src/std/bigint.d @@ -63,8 +63,8 @@ public: * Throws: * $(REF ConvException, std,conv) if the string doesn't represent a valid number */ - this(Range)(Range s) if ( - isBidirectionalRange!Range && + this(Range)(Range s) + if (isBidirectionalRange!Range && isSomeChar!(ElementType!Range) && !isInfinite!Range && !isNarrowString!Range) @@ -160,8 +160,8 @@ public: * (ignored when magnitude is zero) * magnitude = a finite range of unsigned integers */ - this(Range)(bool isNegative, Range magnitude) if ( - isInputRange!Range && + this(Range)(bool isNegative, Range magnitude) + if (isInputRange!Range && isUnsigned!(ElementType!Range) && (hasLength!Range || isForwardRange!Range) && !isInfinite!Range) @@ -181,7 +181,8 @@ public: } /// Construct a `BigInt` from a built-in integral type. - this(T)(T x) pure nothrow @safe if (isIntegral!T) + this(T)(T x) pure nothrow @safe + if (isIntegral!T) { data = data.init; // @@@: Workaround for compiler bug opAssign(x); @@ -196,7 +197,8 @@ public: } /// Construct a `BigInt` from another `BigInt`. - this(T)(T x) pure nothrow @safe if (is(immutable T == immutable BigInt)) + this(T)(T x) pure nothrow @safe + if (is(immutable T == immutable BigInt)) { opAssign(x); } @@ -210,7 +212,8 @@ public: } /// Assignment from built-in integer types. - BigInt opAssign(T)(T x) pure nothrow @safe if (isIntegral!T) + BigInt opAssign(T)(T x) pure nothrow @safe + if (isIntegral!T) { data = cast(ulong) absUnsign(x); sign = (x < 0); @@ -247,8 +250,8 @@ public: * `BigInt op= integer`. */ BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope - if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" - || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T) + if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" + || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T) { ulong u = absUnsign(y); @@ -436,8 +439,7 @@ public: * Implements assignment operators of the form `BigInt op= BigInt`. */ BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope - if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%") - && is (T: BigInt)) + if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%") && is (T: BigInt)) { static if (op == "+") { @@ -494,9 +496,8 @@ public: * Implements binary operators between `BigInt`s. */ BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope - if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" || - op=="/" || op=="%") - && is (T: BigInt)) + if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" || + op=="/" || op=="%") && is (T: BigInt)) { BigInt r = this; return r.opOpAssign!(op)(y); @@ -515,9 +516,9 @@ public: * Implements binary operators between `BigInt`'s and built-in integers. */ BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope - if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || - op=="^"|| op==">>" || op=="<<" || op=="^^") - && isIntegral!T) + if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || + op=="^"|| op==">>" || op=="<<" || op=="^^") + && isIntegral!T) { BigInt r = this; r.opOpAssign!(op)(y); @@ -546,7 +547,7 @@ public: ) */ auto opBinary(string op, T)(T y) pure nothrow @safe const - if (op == "%" && isIntegral!T) + if (op == "%" && isIntegral!T) { assert(y != 0, "% 0 not allowed"); @@ -602,7 +603,7 @@ public: `BigInt` on the right-hand side. */ BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const - if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T) + if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T) { return opBinary!(op)(y); } @@ -627,7 +628,7 @@ public: // BigInt = integer op BigInt /// ditto BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const - if (op == "-" && isIntegral!T) + if (op == "-" && isIntegral!T) { ulong u = absUnsign(y); BigInt r; @@ -643,7 +644,7 @@ public: // integer = integer op BigInt /// ditto T opBinaryRight(string op, T)(T x) pure nothrow @safe const - if ((op=="%" || op=="/") && isIntegral!T) + if ((op=="%" || op=="/") && isIntegral!T) { checkDivByZero(); @@ -669,7 +670,8 @@ public: /** Implements `BigInt` unary operators. */ - BigInt opUnary(string op)() pure nothrow @safe const if (op=="+" || op=="-" || op=="~") + BigInt opUnary(string op)() pure nothrow @safe const + if (op=="+" || op=="-" || op=="~") { static if (op=="-") { @@ -687,7 +689,8 @@ public: // non-const unary operations /// ditto - BigInt opUnary(string op)() pure nothrow @safe if (op=="++" || op=="--") + BigInt opUnary(string op)() pure nothrow @safe + if (op=="++" || op=="--") { static if (op=="++") { @@ -721,7 +724,8 @@ public: } /// ditto - bool opEquals(T)(const T y) const pure nothrow @nogc @safe if (isIntegral!T) + bool opEquals(T)(const T y) const pure nothrow @nogc @safe + if (isIntegral!T) { if (sign != (y<0)) return 0; @@ -729,7 +733,8 @@ public: } /// ditto - bool opEquals(T)(const T y) const pure nothrow @nogc if (isFloatingPoint!T) + bool opEquals(T)(const T y) const pure nothrow @nogc + if (isFloatingPoint!T) { return 0 == opCmp(y); } @@ -896,7 +901,8 @@ public: /** Implements casting to floating point types. */ - T opCast(T)() @safe nothrow @nogc const if (isFloatingPoint!T) + T opCast(T)() @safe nothrow @nogc const + if (isFloatingPoint!T) { return toFloat!(T, "nearest"); } @@ -1090,7 +1096,8 @@ public: } /// ditto - int opCmp(T)(const T y) pure nothrow @nogc @safe const if (isIntegral!T) + int opCmp(T)(const T y) pure nothrow @nogc @safe const + if (isIntegral!T) { if (sign != (y<0) ) return sign ? -1 : 1; @@ -1098,7 +1105,8 @@ public: return sign? -cmp: cmp; } /// ditto - int opCmp(T)(const T y) nothrow @nogc @safe const if (isFloatingPoint!T) + int opCmp(T)(const T y) nothrow @nogc @safe const + if (isFloatingPoint!T) { import core.bitop : bsr; import std.math.operations : cmp; diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d index 0993d34..15211a3 100644 --- a/libphobos/src/std/bitmanip.d +++ b/libphobos/src/std/bitmanip.d @@ -1925,7 +1925,7 @@ public: * Support for unary operator ~ for `BitArray`. */ BitArray opUnary(string op)() const pure nothrow - if (op == "~") + if (op == "~") { auto dim = this.dim; @@ -1962,7 +1962,7 @@ public: * Support for binary bitwise operators for `BitArray`. */ BitArray opBinary(string op)(const BitArray e2) const pure nothrow - if (op == "-" || op == "&" || op == "|" || op == "^") + if (op == "-" || op == "&" || op == "|" || op == "^") in { assert(e2.length == _len, "e2 must have the same length as this"); @@ -2064,7 +2064,7 @@ public: * Support for operator op= for `BitArray`. */ BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow return scope - if (op == "-" || op == "&" || op == "|" || op == "^") + if (op == "-" || op == "&" || op == "|" || op == "^") in { assert(e2.length == _len, "e2 must have the same length as this"); @@ -2185,7 +2185,7 @@ public: * concatenation semantics are not followed) */ BitArray opOpAssign(string op)(bool b) pure nothrow return scope - if (op == "~") + if (op == "~") { length = _len + 1; this[_len - 1] = b; @@ -2215,7 +2215,7 @@ public: * ditto */ BitArray opOpAssign(string op)(BitArray b) pure nothrow return scope - if (op == "~") + if (op == "~") { auto istart = _len; length = _len + b.length; @@ -2249,7 +2249,7 @@ public: * Support for binary operator ~ for `BitArray`. */ BitArray opBinary(string op)(bool b) const pure nothrow - if (op == "~") + if (op == "~") { BitArray r; @@ -2261,7 +2261,7 @@ public: /** ditto */ BitArray opBinaryRight(string op)(bool b) const pure nothrow - if (op == "~") + if (op == "~") { BitArray r; @@ -2274,7 +2274,7 @@ public: /** ditto */ BitArray opBinary(string op)(BitArray b) const pure nothrow - if (op == "~") + if (op == "~") { BitArray r; @@ -2398,7 +2398,7 @@ public: * preserve bits past the end of the array.) */ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow - if (op == "<<") + if (op == "<<") { size_t wordsToShift = nbits / bitsPerSizeT; size_t bitsToShift = nbits % bitsPerSizeT; @@ -2432,7 +2432,7 @@ public: * preserve bits past the end of the array.) */ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow - if (op == ">>") + if (op == ">>") { size_t wordsToShift = nbits / bitsPerSizeT; size_t bitsToShift = nbits % bitsPerSizeT; @@ -2945,58 +2945,6 @@ if (isIntegral!T || isSomeChar!T || isBoolean!T) } -private union EndianSwapper(T) -if (canSwapEndianness!T) -{ - T value; - ubyte[T.sizeof] array; - - static if (is(immutable FloatingPointTypeOf!(T) == immutable float)) - uint intValue; - else static if (is(immutable FloatingPointTypeOf!(T) == immutable double)) - ulong intValue; - -} - -// Can't use EndianSwapper union during CTFE. -private auto ctfeRead(T)(const ubyte[T.sizeof] array) -if (__traits(isIntegral, T)) -{ - Unqual!T result; - version (LittleEndian) - foreach_reverse (b; array) - result = cast() cast(T) ((result << 8) | b); - else - foreach (b; array) - result = cast() cast(T) ((result << 8) | b); - return cast(T) result; -} - -// Can't use EndianSwapper union during CTFE. -private auto ctfeBytes(T)(const T value) -if (__traits(isIntegral, T)) -{ - ubyte[T.sizeof] result; - Unqual!T tmp = value; - version (LittleEndian) - { - foreach (i; 0 .. T.sizeof) - { - result[i] = cast(ubyte) tmp; - tmp = cast() cast(T) (tmp >>> 8); - } - } - else - { - foreach_reverse (i; 0 .. T.sizeof) - { - result[i] = cast(ubyte) tmp; - tmp = cast()(T) (tmp >>> 8); - } - } - return result; -} - /++ Converts the given value from the native endianness to big endian and returns it as a `ubyte[n]` where `n` is the size of the given type. @@ -3010,13 +2958,21 @@ if (__traits(isIntegral, T)) and therefore could vary from machine to machine (which could make it unusable if you tried to transfer it to another machine). +/ -auto nativeToBigEndian(T)(const T val) @safe pure nothrow @nogc +auto nativeToBigEndian(T)(const T val) @trusted pure nothrow @nogc if (canSwapEndianness!T) { - version (LittleEndian) - return nativeToEndianImpl!true(val); + static if (isFloatOrDouble!T) + return nativeToBigEndian(*cast(const UnsignedOfSize!(T.sizeof)*) &val); else - return nativeToEndianImpl!false(val); + { + enum len = T.sizeof; + ubyte[len] retval; + + static foreach (i; 0 .. len) + retval[i] = cast(ubyte)(val >> (len - i - 1) * 8); + + return retval; + } } /// @@ -3043,26 +2999,6 @@ if (canSwapEndianness!T) assert(cd == bigEndianToNative!double(swappedCD)); } -private auto nativeToEndianImpl(bool swap, T)(const T val) @safe pure nothrow @nogc -if (__traits(isIntegral, T)) -{ - if (!__ctfe) - { - static if (swap) - return EndianSwapper!T(swapEndian(val)).array; - else - return EndianSwapper!T(val).array; - } - else - { - // Can't use EndianSwapper in CTFE. - static if (swap) - return ctfeBytes(swapEndian(val)); - else - return ctfeBytes(val); - } -} - @safe unittest { import std.meta; @@ -3149,13 +3085,25 @@ if (__traits(isIntegral, T)) because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values). +/ -T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc +T bigEndianToNative(T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc if (canSwapEndianness!T && n == T.sizeof) { - version (LittleEndian) - return endianToNativeImpl!(true, T, n)(val); + static if (isFloatOrDouble!T) + { + auto retval = bigEndianToNative!(UnsignedOfSize!(T.sizeof))(val); + return *cast(const T*) &retval; + } else - return endianToNativeImpl!(false, T, n)(val); + { + enum len = T.sizeof; + alias U = UnsignedOfSize!len; + U retval; + + static foreach (i; 0 .. len) + retval |= (cast(U) val[i]) << (len - i - 1) * 8; + + return cast(T) retval; + } } /// @@ -3179,13 +3127,21 @@ if (canSwapEndianness!T && n == T.sizeof) because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values). +/ -auto nativeToLittleEndian(T)(const T val) @safe pure nothrow @nogc +auto nativeToLittleEndian(T)(const T val) @trusted pure nothrow @nogc if (canSwapEndianness!T) { - version (BigEndian) - return nativeToEndianImpl!true(val); + static if (isFloatOrDouble!T) + return nativeToLittleEndian(*cast(const UnsignedOfSize!(T.sizeof)*) &val); else - return nativeToEndianImpl!false(val); + { + enum len = T.sizeof; + ubyte[len] retval; + + static foreach (i; 0 .. len) + retval[i] = cast(ubyte)(val >> i * 8); + + return retval; + } } /// @@ -3195,9 +3151,21 @@ if (canSwapEndianness!T) ubyte[4] swappedI = nativeToLittleEndian(i); assert(i == littleEndianToNative!int(swappedI)); + float f = 123.45f; + ubyte[4] swappedF = nativeToLittleEndian(f); + assert(f == littleEndianToNative!float(swappedF)); + + const float cf = 123.45f; + ubyte[4] swappedCF = nativeToLittleEndian(cf); + assert(cf == littleEndianToNative!float(swappedCF)); + double d = 123.45; ubyte[8] swappedD = nativeToLittleEndian(d); assert(d == littleEndianToNative!double(swappedD)); + + const double cd = 123.45; + ubyte[8] swappedCD = nativeToLittleEndian(cd); + assert(cd == littleEndianToNative!double(swappedCD)); } @safe unittest @@ -3259,13 +3227,25 @@ if (canSwapEndianness!T) and therefore could vary from machine to machine (which could make it unusable if you tried to transfer it to another machine). +/ -T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc +T littleEndianToNative(T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc if (canSwapEndianness!T && n == T.sizeof) { - version (BigEndian) - return endianToNativeImpl!(true, T, n)(val); + static if (isFloatOrDouble!T) + { + auto retval = littleEndianToNative!(UnsignedOfSize!(T.sizeof))(val); + return *cast(const T*) &retval; + } else - return endianToNativeImpl!(false, T, n)(val); + { + enum len = T.sizeof; + alias U = UnsignedOfSize!len; + U retval; + + static foreach (i; 0 .. len) + retval |= (cast(U) val[i]) << i * 8; + + return cast(T) retval; + } } /// @@ -3280,70 +3260,6 @@ if (canSwapEndianness!T && n == T.sizeof) assert(c == littleEndianToNative!dchar(swappedC)); } -private T endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @nogc nothrow pure @trusted -if (__traits(isIntegral, T) && n == T.sizeof) -{ - if (!__ctfe) - { - EndianSwapper!T es = { array: val }; - static if (swap) - return swapEndian(es.value); - else - return es.value; - } - else - { - static if (swap) - return swapEndian(ctfeRead!T(val)); - else - return ctfeRead!T(val); - } -} - -private auto nativeToEndianImpl(bool swap, T)(const T val) @trusted pure nothrow @nogc -if (isFloatOrDouble!T) -{ - if (!__ctfe) - { - EndianSwapper!T es = EndianSwapper!T(val); - static if (swap) - es.intValue = swapEndian(es.intValue); - return es.array; - } - else - { - static if (T.sizeof == 4) - uint intValue = *cast(const uint*) &val; - else static if (T.sizeof == 8) - ulong intValue = *cast(const ulong*) & val; - static if (swap) - intValue = swapEndian(intValue); - return ctfeBytes(intValue); - } -} - -private auto endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc -if (isFloatOrDouble!T && n == T.sizeof) -{ - if (!__ctfe) - { - EndianSwapper!T es = { array: val }; - static if (swap) - es.intValue = swapEndian(es.intValue); - return es.value; - } - else - { - static if (n == 4) - uint x = ctfeRead!uint(val); - else static if (n == 8) - ulong x = ctfeRead!ulong(val); - static if (swap) - x = swapEndian(x); - return *cast(T*) &x; - } -} - private template isFloatOrDouble(T) { enum isFloatOrDouble = isFloatingPoint!T && @@ -3405,6 +3321,42 @@ private template canSwapEndianness(T) } } +private template UnsignedOfSize(size_t n) +{ + static if (n == 8) + alias UnsignedOfSize = ulong; + else static if (n == 4) + alias UnsignedOfSize = uint; + else static if (n == 2) + alias UnsignedOfSize = ushort; + else static if (n == 1) + alias UnsignedOfSize = ubyte; + else + alias UnsignedOfSize = void; +} + +@safe unittest +{ + static assert(is(UnsignedOfSize!(byte.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(ubyte.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(short.sizeof) == ushort)); + static assert(is(UnsignedOfSize!(ushort.sizeof) == ushort)); + static assert(is(UnsignedOfSize!(int.sizeof) == uint)); + static assert(is(UnsignedOfSize!(uint.sizeof) == uint)); + static assert(is(UnsignedOfSize!(long.sizeof) == ulong)); + static assert(is(UnsignedOfSize!(ulong.sizeof) == ulong)); + + static assert(is(UnsignedOfSize!(bool.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(char.sizeof) == ubyte)); + static assert(is(UnsignedOfSize!(wchar.sizeof) == ushort)); + static assert(is(UnsignedOfSize!(dchar.sizeof) == uint)); + + static assert(is(UnsignedOfSize!(float.sizeof) == uint)); + static assert(is(UnsignedOfSize!(double.sizeof) == ulong)); + + static assert(is(UnsignedOfSize!10 == void)); +} + /++ Takes a range of `ubyte`s and converts the first `T.sizeof` bytes to `T`. The value returned is converted from the given endianness to the diff --git a/libphobos/src/std/checkedint.d b/libphobos/src/std/checkedint.d index cec1dc1..630ae41 100644 --- a/libphobos/src/std/checkedint.d +++ b/libphobos/src/std/checkedint.d @@ -3362,12 +3362,14 @@ version (StdUnittest) private struct CountOverflows static struct Hook1 { uint calls; - auto hookOpUnary(string op, T)(T value) if (op == "-") + auto hookOpUnary(string op, T)(T value) + if (op == "-") { ++calls; return T(42); } - auto hookOpUnary(string op, T)(T value) if (op == "~") + auto hookOpUnary(string op, T)(T value) + if (op == "~") { ++calls; return T(43); @@ -3383,12 +3385,14 @@ version (StdUnittest) private struct CountOverflows static struct Hook2 { uint calls; - void hookOpUnary(string op, T)(ref T value) if (op == "++") + void hookOpUnary(string op, T)(ref T value) + if (op == "++") { ++calls; --value; } - void hookOpUnary(string op, T)(ref T value) if (op == "--") + void hookOpUnary(string op, T)(ref T value) + if (op == "--") { ++calls; ++value; diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d index 60746f9..01e8dd2 100644 --- a/libphobos/src/std/complex.d +++ b/libphobos/src/std/complex.d @@ -156,7 +156,7 @@ if (isFloatingPoint!T) /// ditto void toString(Writer, Char)(scope Writer w, scope const ref FormatSpec!Char formatSpec) const - if (isOutputRange!(Writer, const(Char)[])) + if (isOutputRange!(Writer, const(Char)[])) { import std.format.write : formatValue; import std.math.traits : signbit; @@ -231,14 +231,14 @@ if (isFloatingPoint!T) // +complex Complex opUnary(string op)() const - if (op == "+") + if (op == "+") { return this; } // -complex Complex opUnary(string op)() const - if (op == "-") + if (op == "-") { return Complex(-re, -im); } @@ -255,7 +255,7 @@ if (isFloatingPoint!T) // complex op numeric Complex!(CommonType!(T,R)) opBinary(string op, R)(const R r) const - if (isNumeric!R) + if (isNumeric!R) { alias C = typeof(return); auto w = C(this.re, this.im); @@ -264,21 +264,21 @@ if (isFloatingPoint!T) // numeric + complex, numeric * complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if ((op == "+" || op == "*") && (isNumeric!R)) + if ((op == "+" || op == "*") && (isNumeric!R)) { return opBinary!(op)(r); } // numeric - complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if (op == "-" && isNumeric!R) + if (op == "-" && isNumeric!R) { return Complex(r - re, -im); } // numeric / complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const - if (op == "/" && isNumeric!R) + if (op == "/" && isNumeric!R) { version (FastMath) { @@ -320,7 +320,7 @@ if (isFloatingPoint!T) // numeric ^^ complex Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R lhs) const - if (op == "^^" && isNumeric!R) + if (op == "^^" && isNumeric!R) { import core.math : cos, sin; import std.math.exponential : exp, log; @@ -349,7 +349,7 @@ if (isFloatingPoint!T) // complex += complex, complex -= complex ref Complex opOpAssign(string op, C)(const C z) - if ((op == "+" || op == "-") && is(C R == Complex!R)) + if ((op == "+" || op == "-") && is(C R == Complex!R)) { mixin ("re "~op~"= z.re;"); mixin ("im "~op~"= z.im;"); @@ -358,7 +358,7 @@ if (isFloatingPoint!T) // complex *= complex ref Complex opOpAssign(string op, C)(const C z) - if (op == "*" && is(C R == Complex!R)) + if (op == "*" && is(C R == Complex!R)) { auto temp = re*z.re - im*z.im; im = im*z.re + re*z.im; @@ -368,7 +368,7 @@ if (isFloatingPoint!T) // complex /= complex ref Complex opOpAssign(string op, C)(const C z) - if (op == "/" && is(C R == Complex!R)) + if (op == "/" && is(C R == Complex!R)) { version (FastMath) { @@ -409,7 +409,7 @@ if (isFloatingPoint!T) // complex ^^= complex ref Complex opOpAssign(string op, C)(const C z) - if (op == "^^" && is(C R == Complex!R)) + if (op == "^^" && is(C R == Complex!R)) { import core.math : cos, sin; import std.math.exponential : exp, log; @@ -425,7 +425,7 @@ if (isFloatingPoint!T) // complex += numeric, complex -= numeric ref Complex opOpAssign(string op, U : T)(const U a) - if (op == "+" || op == "-") + if (op == "+" || op == "-") { mixin ("re "~op~"= a;"); return this; @@ -433,7 +433,7 @@ if (isFloatingPoint!T) // complex *= numeric, complex /= numeric ref Complex opOpAssign(string op, U : T)(const U a) - if (op == "*" || op == "/") + if (op == "*" || op == "/") { mixin ("re "~op~"= a;"); mixin ("im "~op~"= a;"); @@ -442,7 +442,7 @@ if (isFloatingPoint!T) // complex ^^= real ref Complex opOpAssign(string op, R)(const R r) - if (op == "^^" && isFloatingPoint!R) + if (op == "^^" && isFloatingPoint!R) { import core.math : cos, sin; immutable ab = abs(this)^^r; @@ -454,7 +454,7 @@ if (isFloatingPoint!T) // complex ^^= int ref Complex opOpAssign(string op, U)(const U i) - if (op == "^^" && isIntegral!U) + if (op == "^^" && isIntegral!U) { switch (i) { diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d index 267f682..94265a2 100644 --- a/libphobos/src/std/concurrency.d +++ b/libphobos/src/std/concurrency.d @@ -163,7 +163,8 @@ private MsgType type; Variant data; - this(T...)(MsgType t, T vals) if (T.length > 0) + this(T...)(MsgType t, T vals) + if (T.length > 0) { static if (T.length == 1) { diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d index 4fdf13d..728aacd 100644 --- a/libphobos/src/std/container/dlist.d +++ b/libphobos/src/std/container/dlist.d @@ -245,7 +245,8 @@ struct DList(T) /** Constructor taking a number of nodes */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + this(U)(U[] values...) + if (isImplicitlyConvertible!(U, T)) { insertBack(values); } diff --git a/libphobos/src/std/container/package.d b/libphobos/src/std/container/package.d index 763da8b..fc04950 100644 --- a/libphobos/src/std/container/package.d +++ b/libphobos/src/std/container/package.d @@ -801,7 +801,8 @@ Indexing operators yield or modify the value at a specified index. /** $(D k in container) returns true if the given key is in the container. */ - bool opBinaryRight(string op)(KeyType k) if (op == "in") + bool opBinaryRight(string op)(KeyType k) + if (op == "in") { assert(0, "Not implemented"); } @@ -843,13 +844,15 @@ define `opBinary`. Complexity: $(BIGOH n + m), where m is the number of elements in $(D stuff) */ - TotalContainer opBinary(string op)(Stuff rhs) if (op == "~") + TotalContainer opBinary(string op)(Stuff rhs) + if (op == "~") { assert(0, "Not implemented"); } /// ditto - TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~") + TotalContainer opBinaryRight(string op)(Stuff lhs) + if (op == "~") { assert(0, "Not implemented"); } @@ -857,7 +860,8 @@ stuff) /** Forwards to $(D insertAfter(this[], stuff)). */ - void opOpAssign(string op)(Stuff stuff) if (op == "~") + void opOpAssign(string op)(Stuff stuff) + if (op == "~") { assert(0, "Not implemented"); } diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d index 9bd8d27..5369702 100644 --- a/libphobos/src/std/container/rbtree.d +++ b/libphobos/src/std/container/rbtree.d @@ -1057,7 +1057,8 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) Complexity: $(BIGOH log(n)) +/ - bool opBinaryRight(string op)(Elem e) const if (op == "in") + bool opBinaryRight(string op)(Elem e) const + if (op == "in") { return _find(e) !is null; } @@ -1261,7 +1262,8 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) * * Complexity: $(BIGOH log(n)) */ - size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem)) + size_t stableInsert(Stuff)(Stuff stuff) + if (isImplicitlyConvertible!(Stuff, Elem)) { static if (allowDuplicates) { @@ -1283,8 +1285,8 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) * Complexity: $(BIGOH m * log(n)) */ size_t stableInsert(Stuff)(scope Stuff stuff) - if (isInputRange!Stuff && - isImplicitlyConvertible!(ElementType!Stuff, Elem)) + if (isInputRange!Stuff && + isImplicitlyConvertible!(ElementType!Stuff, Elem)) { size_t result = 0; static if (allowDuplicates) @@ -1534,7 +1536,7 @@ assert(equal(rbt[], [5])); -------------------- +/ size_t removeKey(U...)(U elems) - if (allSatisfy!(isImplicitlyConvertibleToElem, U)) + if (allSatisfy!(isImplicitlyConvertibleToElem, U)) { Elem[U.length] toRemove = [elems]; return removeKey(toRemove[]); @@ -1542,7 +1544,7 @@ assert(equal(rbt[], [5])); /++ Ditto +/ size_t removeKey(U)(scope U[] elems) - if (isImplicitlyConvertible!(U, Elem)) + if (isImplicitlyConvertible!(U, Elem)) { immutable lenBefore = length; @@ -1564,9 +1566,9 @@ assert(equal(rbt[], [5])); /++ Ditto +/ size_t removeKey(Stuff)(Stuff stuff) - if (isInputRange!Stuff && - isImplicitlyConvertible!(ElementType!Stuff, Elem) && - !isDynamicArray!Stuff) + if (isInputRange!Stuff && + isImplicitlyConvertible!(ElementType!Stuff, Elem) && + !isDynamicArray!Stuff) { import std.array : array; //We use array in case stuff is a Range from this RedBlackTree - either @@ -1873,7 +1875,8 @@ assert(equal(rbt[], [5])); /** * Constructor. Pass in a range of elements to initialize the tree with. */ - this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) + this(Stuff)(Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) { _setup(); stableInsert(stuff); diff --git a/libphobos/src/std/container/slist.d b/libphobos/src/std/container/slist.d index 0b504b4..ef6fbf0 100644 --- a/libphobos/src/std/container/slist.d +++ b/libphobos/src/std/container/slist.d @@ -182,7 +182,8 @@ if (!is(T == shared)) /** Constructor taking a number of nodes */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + this(U)(U[] values...) + if (isImplicitlyConvertible!(U, T)) { insertFront(values); } diff --git a/libphobos/src/std/container/util.d b/libphobos/src/std/container/util.d index cc273a2..0b883d0 100644 --- a/libphobos/src/std/container/util.d +++ b/libphobos/src/std/container/util.d @@ -109,14 +109,14 @@ if (!is(Container)) import std.traits : isDynamicArray; auto make(Range)(Range range) - if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range) + if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range) { import std.range : ElementType; return .make!(Container!(ElementType!Range, Args))(range); } auto make(T)(T[] items...) - if (!isInfinite!T) + if (!isInfinite!T) { return .make!(Container!(T, Args))(items); } diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 3aa73c6..9c9d8db 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -205,21 +205,21 @@ $(PRE $(I UnsignedInteger): template to(T) { T to(A...)(A args) - if (A.length > 0) + if (A.length > 0) { return toImpl!T(args); } // Fix https://issues.dlang.org/show_bug.cgi?id=6175 T to(S)(ref S arg) - if (isStaticArray!S) + if (isStaticArray!S) { return toImpl!T(arg); } // Fix https://issues.dlang.org/show_bug.cgi?id=16108 T to(S)(ref S arg) - if (isAggregateType!S && !isCopyable!S) + if (isAggregateType!S && !isCopyable!S) { return toImpl!T(arg); } @@ -917,9 +917,22 @@ if (!is(S : T) && auto result = ()@trusted{ return cast(T) value; }(); if (!result && value) { - throw new ConvException("Cannot convert object of static type " - ~S.classinfo.name~" and dynamic type "~value.classinfo.name - ~" to type "~T.classinfo.name); + string name(TypeInfo ti) @trusted + { + while (auto tc = (cast(TypeInfo_Const) ti)) + { + ti = tc.base; + } + if (auto tinf = cast(TypeInfo_Interface) ti) + { + ti = tinf.info; + } + TypeInfo_Class tc = cast(TypeInfo_Class) ti; + assert(tc); + return tc.name; + } + throw new ConvException("Cannot convert object of static type " ~ + name(typeid(S)) ~ " and dynamic type " ~ name(typeid(value)) ~ " to type " ~ name(typeid(T))); } return result; } diff --git a/libphobos/src/std/datetime/date.d b/libphobos/src/std/datetime/date.d index c757b1e..0f417b1 100644 --- a/libphobos/src/std/datetime/date.d +++ b/libphobos/src/std/datetime/date.d @@ -1075,7 +1075,7 @@ public: +/ ref DateTime add(string units) (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc - if (units == "years" || units == "months") + if (units == "years" || units == "months") { _date.add!units(value, allowOverflow); return this; @@ -1140,7 +1140,7 @@ public: +/ ref DateTime roll(string units) (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc - if (units == "years" || units == "months") + if (units == "years" || units == "months") { _date.roll!units(value, allowOverflow); return this; @@ -1209,7 +1209,7 @@ public: A reference to the `DateTime` (`this`). +/ ref DateTime roll(string units)(long value) @safe pure nothrow @nogc - if (units == "days") + if (units == "days") { _date.roll!"days"(value); return this; @@ -1250,9 +1250,9 @@ public: /// ditto ref DateTime roll(string units)(long value) @safe pure nothrow @nogc - if (units == "hours" || - units == "minutes" || - units == "seconds") + if (units == "hours" || + units == "minutes" || + units == "seconds") { _tod.roll!units(value); return this; @@ -2138,7 +2138,7 @@ public: this $(LREF DateTime). +/ DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { DateTime retval = this; immutable seconds = duration.total!"seconds"; @@ -2233,7 +2233,7 @@ public: $(LREF DateTime). +/ ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { import core.time : convert; import std.format : format; @@ -2339,7 +2339,7 @@ public: ) +/ Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc - if (op == "-") + if (op == "-") { immutable dateResult = _date - rhs.date; immutable todResult = _tod - rhs._tod; @@ -3151,7 +3151,7 @@ public: be valid. +/ static DateTime fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : countUntil; import std.exception : enforce; @@ -3252,7 +3252,7 @@ public: would not be valid. +/ static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : countUntil; import std.exception : enforce; @@ -3353,7 +3353,7 @@ public: would not be valid. +/ static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : countUntil; import std.exception : enforce; @@ -4483,7 +4483,7 @@ public: +/ @safe pure nothrow @nogc ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "years") + if (units == "years") { _year += value; @@ -4724,7 +4724,7 @@ public: // Shares documentation with "years" version. @safe pure nothrow @nogc ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "months") + if (units == "months") { auto years = months / 12; months %= 12; @@ -5268,7 +5268,7 @@ public: +/ @safe pure nothrow @nogc ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "years") + if (units == "years") { return add!"years"(value, allowOverflow); } @@ -5313,7 +5313,7 @@ public: // Shares documentation with "years" version. @safe pure nothrow @nogc ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (units == "months") + if (units == "months") { months %= 12; auto newMonth = _month + months; @@ -5910,7 +5910,7 @@ public: A reference to the `Date` (`this`). +/ ref Date roll(string units)(long days) @safe pure nothrow @nogc - if (units == "days") + if (units == "days") { immutable limit = maxDay(_year, _month); days %= limit; @@ -6148,7 +6148,7 @@ public: this $(LREF Date). +/ Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { Date retval = this; immutable days = duration.total!"days"; @@ -6238,7 +6238,7 @@ public: this $(LREF Date). +/ ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { immutable days = duration.total!"days"; mixin("return _addDays(" ~ op ~ "days);"); @@ -6313,7 +6313,7 @@ public: ) +/ Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc - if (op == "-") + if (op == "-") { import core.time : dur; return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); @@ -7621,7 +7621,7 @@ public: valid. +/ static Date fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith; import std.conv : to, text, ConvException; @@ -7764,7 +7764,7 @@ public: would not be valid. +/ static Date fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : startsWith; import std.conv : to, ConvException; @@ -7902,7 +7902,7 @@ public: be valid. +/ static Date fromSimpleString(S)(scope const S simpleString) @safe pure - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : startsWith; import std.conv : to, ConvException; @@ -8606,7 +8606,7 @@ public: A reference to the `TimeOfDay` (`this`). +/ ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc - if (units == "hours") + if (units == "hours") { import core.time : dur; return this += dur!"hours"(value); @@ -8655,7 +8655,7 @@ public: /// ditto ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc - if (units == "minutes" || units == "seconds") + if (units == "minutes" || units == "seconds") { import std.format : format; @@ -8851,7 +8851,7 @@ public: this $(LREF TimeOfDay). +/ TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { TimeOfDay retval = this; immutable seconds = duration.total!"seconds"; @@ -8938,7 +8938,7 @@ public: this $(LREF TimeOfDay). +/ ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc - if (op == "+" || op == "-") + if (op == "+" || op == "-") { immutable seconds = duration.total!"seconds"; mixin("return _addSeconds(" ~ op ~ "seconds);"); @@ -9004,7 +9004,7 @@ public: rhs = The $(LREF TimeOfDay) to subtract from this one. +/ Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc - if (op == "-") + if (op == "-") { immutable lhsSec = _hour * 3600 + _minute * 60 + _second; immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; @@ -9201,7 +9201,7 @@ public: not be valid. +/ static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.conv : to, text, ConvException; import std.exception : enforce; @@ -9326,7 +9326,7 @@ public: would not be valid. +/ static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.conv : ConvException, text, to; import std.string : strip; diff --git a/libphobos/src/std/datetime/interval.d b/libphobos/src/std/datetime/interval.d index d787e3a..832a2cb 100644 --- a/libphobos/src/std/datetime/interval.d +++ b/libphobos/src/std/datetime/interval.d @@ -137,7 +137,7 @@ public: -------------------- +/ this(U)(scope const TP begin, scope const U end) pure - if (is(immutable TP == immutable U)) + if (is(immutable TP == immutable U)) { if (!_valid(begin, end)) throw new DateTimeException("Arguments would result in an invalid Interval."); @@ -162,7 +162,7 @@ public: -------------------- +/ this(D)(scope const TP begin, scope const D duration) pure - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _begin = cast(TP) begin; _end = begin + duration; @@ -1118,7 +1118,7 @@ public: -------------------- +/ void shift(D)(D duration) pure - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _enforceNotEmpty(); @@ -1168,7 +1168,7 @@ public: -------------------- +/ void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { _enforceNotEmpty(); @@ -1215,7 +1215,7 @@ public: -------------------- +/ void expand(D)(D duration, Direction dir = Direction.both) pure - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _enforceNotEmpty(); @@ -3919,7 +3919,7 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); -------------------- +/ void shift(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _begin += duration; } @@ -3960,7 +3960,7 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); -------------------- +/ void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto begin = _begin; @@ -3992,7 +3992,7 @@ assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); -------------------- +/ void expand(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) + if (__traits(compiles, begin + duration)) { _begin -= duration; } @@ -4028,7 +4028,7 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); -------------------- +/ void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto begin = _begin; @@ -6145,7 +6145,7 @@ assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); -------------------- +/ void shift(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) + if (__traits(compiles, end + duration)) { _end += duration; } @@ -6185,7 +6185,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -------------------- +/ void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto end = _end; @@ -6217,7 +6217,7 @@ assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); -------------------- +/ void expand(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) + if (__traits(compiles, end + duration)) { _end += duration; } @@ -6253,7 +6253,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -------------------- +/ void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) - if (isIntegral!T) + if (isIntegral!T) { auto end = _end; diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d index a2e52ae..fd2a9e1 100644 --- a/libphobos/src/std/datetime/systime.d +++ b/libphobos/src/std/datetime/systime.d @@ -2444,7 +2444,7 @@ public: this SysTime. +/ T toUnixTime(T = time_t)() @safe const pure nothrow scope - if (is(T == int) || is(T == long)) + if (is(T == int) || is(T == long)) { return stdTimeToUnixTime!T(_stdTime); } @@ -2792,7 +2792,7 @@ public: causing the month to increment. +/ ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope - if (units == "years" || units == "months") + if (units == "years" || units == "months") { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -4830,7 +4830,7 @@ public: $(LREF SysTime). +/ ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "days") + if (units == "days") { auto hnsecs = adjTime; auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -5195,7 +5195,7 @@ public: // Shares documentation with "days" version. ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "hours" || units == "minutes" || units == "seconds") + if (units == "hours" || units == "minutes" || units == "seconds") { try { @@ -5871,7 +5871,7 @@ public: // Shares documentation with "days" version. ref SysTime roll(string units)(long value) @safe nothrow scope - if (units == "msecs" || units == "usecs" || units == "hnsecs") + if (units == "msecs" || units == "usecs" || units == "hnsecs") { auto hnsecs = adjTime; immutable days = splitUnitsFromHNSecs!"days"(hnsecs); @@ -6308,7 +6308,7 @@ public: this $(LREF SysTime). +/ SysTime opBinary(string op)(Duration duration) @safe const pure nothrow return scope - if (op == "+" || op == "-") + if (op == "+" || op == "-") { SysTime retval = SysTime(this._stdTime, this._timezone); immutable hnsecs = duration.total!"hnsecs"; @@ -6528,7 +6528,7 @@ public: this $(LREF SysTime). +/ ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow scope - if (op == "+" || op == "-") + if (op == "+" || op == "-") { immutable hnsecs = duration.total!"hnsecs"; mixin("_stdTime " ~ op ~ "= hnsecs;"); @@ -6732,7 +6732,7 @@ public: ) +/ Duration opBinary(string op)(SysTime rhs) @safe const pure nothrow scope - if (op == "-") + if (op == "-") { return dur!"hnsecs"(_stdTime - rhs._stdTime); } @@ -7992,7 +7992,7 @@ public: Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime). +/ Date opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable Date)) + if (is(immutable T == immutable Date)) { return Date(dayOfGregorianCal); } @@ -8033,7 +8033,7 @@ public: $(LREF SysTime). +/ DateTime opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable DateTime)) + if (is(immutable T == immutable DateTime)) { try { @@ -8099,7 +8099,7 @@ public: $(LREF SysTime). +/ TimeOfDay opCast(T)() @safe const nothrow scope - if (is(immutable T == immutable TimeOfDay)) + if (is(immutable T == immutable TimeOfDay)) { try { @@ -8156,7 +8156,7 @@ public: // should be allowed, and it doesn't work without this opCast() since opCast() // has already been defined for other types. SysTime opCast(T)() @safe const pure nothrow scope - if (is(immutable T == immutable SysTime)) + if (is(immutable T == immutable SysTime)) { return SysTime(_stdTime, _timezone); } @@ -8799,7 +8799,7 @@ public: be valid. +/ static SysTime fromISOString(S)(scope const S isoString, immutable TimeZone tz = null) @safe - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith, find; import std.conv : to; @@ -9100,7 +9100,7 @@ public: be valid. +/ static SysTime fromISOExtString(S)(scope const S isoExtString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : countUntil, find; import std.conv : to; @@ -9351,7 +9351,7 @@ public: be valid. +/ static SysTime fromSimpleString(S)(scope const S simpleString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) + if (isSomeString!(S)) { import std.algorithm.searching : find; import std.conv : to; diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d index 4b6f27d..6a1898b 100644 --- a/libphobos/src/std/datetime/timezone.d +++ b/libphobos/src/std/datetime/timezone.d @@ -1550,7 +1550,7 @@ package: isoString = A string which represents a time zone in the ISO format. +/ static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith; import std.conv : text, to, ConvException; @@ -1704,7 +1704,7 @@ package: isoExtString = A string which represents a time zone in the ISO format. +/ static immutable(SimpleTimeZone) fromISOExtString(S)(scope S isoExtString) @safe pure - if (isSomeString!S) + if (isSomeString!S) { import std.algorithm.searching : startsWith; import std.conv : ConvException, to; @@ -2642,7 +2642,7 @@ private: Reads an int from a TZ file. +/ static T readVal(T)(ref File tzFile) @trusted - if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool)) + if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool)) { import std.bitmanip : bigEndianToNative; T[1] buff; @@ -2657,7 +2657,7 @@ private: Reads an array of values from a TZ file. +/ static T readVal(T)(ref File tzFile, size_t length) @trusted - if (isArray!T) + if (isArray!T) { auto buff = new T(length); @@ -2672,7 +2672,7 @@ private: Reads a `TempTTInfo` from a TZ file. +/ static T readVal(T)(ref File tzFile) @safe - if (is(T == TempTTInfo)) + if (is(T == TempTTInfo)) { return TempTTInfo(readVal!int(tzFile), readVal!bool(tzFile), diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d index c3024d7..9b1a7c8 100644 --- a/libphobos/src/std/exception.d +++ b/libphobos/src/std/exception.d @@ -1801,7 +1801,7 @@ expression. @system unittest { import std.format : format; - assert("%s".format.ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException"); + assert("%s".format.ifThrown!Exception(e => typeid(e).name) == "std.format.FormatException"); } //Verify Examples @@ -1834,7 +1834,7 @@ expression. static assert(!__traits(compiles, (new Object()).ifThrown(1))); //Use a lambda to get the thrown object. - assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException"); + assert("%s".format().ifThrown(e => typeid(e).name) == "std.format.FormatException"); } @system unittest diff --git a/libphobos/src/std/experimental/allocator/common.d b/libphobos/src/std/experimental/allocator/common.d index d2efe33..b06fb62 100644 --- a/libphobos/src/std/experimental/allocator/common.d +++ b/libphobos/src/std/experimental/allocator/common.d @@ -67,6 +67,39 @@ unittest } /** +State of an allocator `A`. + +`AllocatorState!(A).sizeof` is zero for `A` being `NullAllocator`, `Mallocator`, +`GCAllocator`, and `MMapAllocator` and typically non-zero for the other. + */ +mixin template AllocatorState(A) +if (isAllocator!A) +{ + static if (stateSize!A == 0) + alias allocator = A.instance; + else + A allocator; +} + +/// +@safe @nogc nothrow pure +unittest +{ + import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mmap_allocator : MmapAllocator; + struct S + { + mixin AllocatorState!NullAllocator n; + mixin AllocatorState!GCAllocator g; + mixin AllocatorState!Mallocator m; + mixin AllocatorState!MmapAllocator p; + } + static assert(S.sizeof == 1); +} + +/** Returns `true` if the `Allocator` has the alignment known at compile time; otherwise it returns `false`. */ diff --git a/libphobos/src/std/experimental/allocator/mallocator.d b/libphobos/src/std/experimental/allocator/mallocator.d index 087dbec..fbe9366 100644 --- a/libphobos/src/std/experimental/allocator/mallocator.d +++ b/libphobos/src/std/experimental/allocator/mallocator.d @@ -118,9 +118,9 @@ struct Mallocator version (CRuntime_Microsoft) { - @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t); - @nogc nothrow private extern(C) void _aligned_free(void *memblock); - @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t); + @nogc nothrow pure private extern(C) void* _aligned_malloc(size_t, size_t); + @nogc nothrow pure private extern(C) void _aligned_free(void *memblock); + @nogc nothrow pure private extern(C) void* _aligned_realloc(void *, size_t, size_t); } /** @@ -138,7 +138,7 @@ struct AlignedMallocator /** Forwards to $(D alignedAllocate(bytes, platformAlignment)). */ - @trusted @nogc nothrow + @trusted @nogc nothrow pure void[] allocate(size_t bytes) shared { if (!bytes) return null; @@ -152,7 +152,7 @@ struct AlignedMallocator `__aligned_malloc`) on Windows. */ version (Posix) - @trusted @nogc nothrow + @trusted @nogc nothrow pure void[] alignedAllocate(size_t bytes, uint a) shared { import core.stdc.errno : ENOMEM, EINVAL; @@ -184,7 +184,7 @@ version (LDC_AddressSanitizer) return result[0 .. bytes]; } else version (Windows) - @trusted @nogc nothrow + @trusted @nogc nothrow pure void[] alignedAllocate(size_t bytes, uint a) shared { auto result = _aligned_malloc(bytes, a); @@ -198,15 +198,15 @@ version (LDC_AddressSanitizer) `__aligned_free(b.ptr)`) on Windows. */ version (Posix) - @system @nogc nothrow + @system @nogc nothrow pure bool deallocate(void[] b) shared { - import core.stdc.stdlib : free; - free(b.ptr); + import core.memory : pureFree; + pureFree(b.ptr); return true; } else version (Windows) - @system @nogc nothrow + @system @nogc nothrow pure bool deallocate(void[] b) shared { _aligned_free(b.ptr); @@ -219,7 +219,7 @@ version (LDC_AddressSanitizer) Should be used with blocks obtained with `allocate` otherwise the custom alignment passed with `alignedAllocate` can be lost. */ - @system @nogc nothrow + @system @nogc nothrow pure bool reallocate(ref void[] b, size_t newSize) shared { return alignedReallocate(b, newSize, alignment); @@ -233,7 +233,7 @@ version (LDC_AddressSanitizer) $(D __aligned_realloc(b.ptr, newSize, a))). */ version (Windows) - @system @nogc nothrow + @system @nogc nothrow pure bool alignedReallocate(ref void[] b, size_t s, uint a) shared { if (!s) @@ -250,7 +250,7 @@ version (LDC_AddressSanitizer) /// ditto version (Posix) - @system @nogc nothrow + @system @nogc nothrow pure bool alignedReallocate(ref void[] b, size_t s, uint a) shared { if (!s) @@ -281,7 +281,7 @@ version (LDC_AddressSanitizer) } /// -@nogc @system nothrow unittest +pure @nogc @system nothrow unittest { auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4, 128); @@ -290,7 +290,7 @@ version (LDC_AddressSanitizer) } version (Posix) -@nogc @system nothrow unittest +pure @nogc @system nothrow unittest { // https://issues.dlang.org/show_bug.cgi?id=16398 // test the "pseudo" alignedReallocate for Posix diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index 2a0d139..c3466ac 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -4794,7 +4794,7 @@ private struct DirIteratorImpl } this(R)(R pathname, SpanMode mode, bool followSymlink) - if (isSomeFiniteCharInputRange!R) + if (isSomeFiniteCharInputRange!R) { _mode = mode; _followSymlink = followSymlink; diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d index 16c7a51..8b60565 100644 --- a/libphobos/src/std/format/internal/write.d +++ b/libphobos/src/std/format/internal/write.d @@ -1935,7 +1935,8 @@ template hasToString(T, Char) static struct G { string toString() {return "";} - void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) + if (isOutputRange!(Writer, string)) {} } static struct H { @@ -1946,7 +1947,8 @@ template hasToString(T, Char) } static struct I { - void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) + if (isOutputRange!(Writer, string)) {} void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) if (isOutputRange!(Writer, string)) {} @@ -2042,7 +2044,8 @@ template hasToString(T, Char) static struct G { string toString() const {return "";} - void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) const + if (isOutputRange!(Writer, string)) {} } static struct H { @@ -2053,7 +2056,8 @@ template hasToString(T, Char) } static struct I { - void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w) const + if (isOutputRange!(Writer, string)) {} void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const if (isOutputRange!(Writer, string)) {} @@ -2603,7 +2607,8 @@ if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(Builtin { int n = 0; alias n this; - T opCast(T) () if (is(T == Frop)) + T opCast(T) () + if (is(T == Frop)) { return Frop(); } diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d index 588a9c8..5022702 100644 --- a/libphobos/src/std/functional.d +++ b/libphobos/src/std/functional.d @@ -445,7 +445,7 @@ if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=") { import std.traits : isIntegral; private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure - if (isIntegral!ElementType1 && isIntegral!ElementType2) + if (isIntegral!ElementType1 && isIntegral!ElementType2) { import std.traits : CommonType; alias T = CommonType!(ElementType1, ElementType2); diff --git a/libphobos/src/std/int128.d b/libphobos/src/std/int128.d index 9289544..c556915 100644 --- a/libphobos/src/std/int128.d +++ b/libphobos/src/std/int128.d @@ -111,7 +111,7 @@ public struct Int128 * Returns: lvalue of result */ Int128 opUnary(string op)() const - if (op == "+") + if (op == "+") { return this; } @@ -121,7 +121,7 @@ public struct Int128 * Returns: lvalue of result */ Int128 opUnary(string op)() const - if (op == "-" || op == "~") + if (op == "-" || op == "~") { static if (op == "-") return Int128(neg(this.data)); @@ -134,7 +134,7 @@ public struct Int128 * Returns: lvalue of result */ Int128 opUnary(string op)() - if (op == "++" || op == "--") + if (op == "++" || op == "--") { static if (op == "++") this.data = inc(this.data); @@ -206,9 +206,9 @@ public struct Int128 * Returns: value after the operation is applied */ Int128 opBinary(string op)(Int128 op2) const - if (op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") { static if (op == "+") return Int128(add(this.data, op2.data)); @@ -236,10 +236,10 @@ public struct Int128 /// ditto Int128 opBinary(string op, Int)(const Int op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(Int : long) && __traits(isIntegral, Int)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) return mixin("this " ~ op ~ " Int128(0, op2)"); @@ -249,20 +249,20 @@ public struct Int128 /// ditto Int128 opBinary(string op, IntLike)(auto ref IntLike op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(IntLike : long) && !__traits(isIntegral, IntLike)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinary!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// ditto Int128 opBinaryRight(string op, Int)(const Int op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(Int : long) && __traits(isIntegral, Int)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(Int : long) && __traits(isIntegral, Int)) { static if (__traits(isUnsigned, Int)) mixin("return Int128(0, op2) " ~ op ~ " this;"); @@ -272,31 +272,31 @@ public struct Int128 /// ditto Int128 opBinaryRight(string op, IntLike)(auto ref IntLike op2) const - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^") && - is(IntLike : long) && !__traits(isIntegral, IntLike)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^") && + is(IntLike : long) && !__traits(isIntegral, IntLike)) { return opBinaryRight!(op)(__traits(getMember, op2, __traits(getAliasThis, IntLike)[0])); } /// ditto Int128 opBinary(string op)(long op2) const - if (op == "<<") + if (op == "<<") { return Int128(shl(this.data, cast(uint) op2)); } /// ditto Int128 opBinary(string op)(long op2) const - if (op == ">>") + if (op == ">>") { return Int128(sar(this.data, cast(uint) op2)); } /// ditto Int128 opBinary(string op)(long op2) const - if (op == ">>>") + if (op == ">>>") { return Int128(shr(this.data, cast(uint) op2)); } @@ -307,10 +307,10 @@ public struct Int128 * Returns: lvalue of updated left operand */ ref Int128 opOpAssign(string op)(Int128 op2) - if (op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^" || - op == "<<" || op == ">>" || op == ">>>") + if (op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^" || + op == "<<" || op == ">>" || op == ">>>") { mixin("this = this " ~ op ~ " op2;"); return this; @@ -318,11 +318,11 @@ public struct Int128 /// ditto ref Int128 opOpAssign(string op, Int)(auto ref Int op2) - if ((op == "+" || op == "-" || - op == "*" || op == "/" || op == "%" || - op == "&" || op == "|" || op == "^" || - op == "<<" || op == ">>" || op == ">>>") - && is(Int : long)) + if ((op == "+" || op == "-" || + op == "*" || op == "/" || op == "%" || + op == "&" || op == "|" || op == "^" || + op == "<<" || op == ">>" || op == ">>>") + && is(Int : long)) { mixin("this = this " ~ op ~ " op2;"); return this; diff --git a/libphobos/src/std/internal/math/biguintcore.d b/libphobos/src/std/internal/math/biguintcore.d index 9df6bb2..9c794b6 100644 --- a/libphobos/src/std/internal/math/biguintcore.d +++ b/libphobos/src/std/internal/math/biguintcore.d @@ -250,7 +250,8 @@ private: data = x; } package(std) // used from: std.bigint - this(T)(T x) pure nothrow @safe scope if (isIntegral!T) + this(T)(T x) pure nothrow @safe scope + if (isIntegral!T) { opAssign(x); } @@ -312,7 +313,8 @@ public: } /// - void opAssign(Tulong)(Tulong u) pure nothrow @safe scope if (is (Tulong == ulong)) + void opAssign(Tulong)(Tulong u) pure nothrow @safe scope + if (is (Tulong == ulong)) { if (u == 0) data = ZERO; else if (u == 1) data = ONE; @@ -356,7 +358,8 @@ public: } /// - int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope if (is (Tulong == ulong)) + int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope + if (is (Tulong == ulong)) { if (data.length > maxBigDigits!Tulong) return 1; @@ -501,8 +504,8 @@ public: } // return false if invalid character found - bool fromHexString(Range)(Range s) scope if ( - isBidirectionalRange!Range && isSomeChar!(ElementType!Range)) + bool fromHexString(Range)(Range s) scope + if (isBidirectionalRange!Range && isSomeChar!(ElementType!Range)) { import std.range : walkLength; @@ -570,8 +573,8 @@ public: } // return true if OK; false if erroneous characters found - bool fromDecimalString(Range)(Range s) scope if ( - isForwardRange!Range && isSomeChar!(ElementType!Range)) + bool fromDecimalString(Range)(Range s) scope + if (isForwardRange!Range && isSomeChar!(ElementType!Range)) { import std.range : walkLength; @@ -596,9 +599,9 @@ public: } void fromMagnitude(Range)(Range magnitude) scope - if (isInputRange!Range - && (isForwardRange!Range || hasLength!Range) - && isUnsigned!(ElementType!Range)) + if (isInputRange!Range + && (isForwardRange!Range || hasLength!Range) + && isUnsigned!(ElementType!Range)) { while (!magnitude.empty && magnitude.front == 0) magnitude.popFront; @@ -711,7 +714,7 @@ public: // return x >> y BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const return scope - if (op == ">>" && is (Tulong == ulong)) + if (op == ">>" && is (Tulong == ulong)) { assert(y > 0, "Can not right shift BigUint by 0"); uint bits = cast(uint) y & BIGDIGITSHIFTMASK; @@ -735,7 +738,7 @@ public: // return x << y BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const scope - if (op == "<<" && is (Tulong == ulong)) + if (op == "<<" && is (Tulong == ulong)) { assert(y > 0, "Can not left shift BigUint by 0"); if (isZero()) return this; @@ -761,8 +764,8 @@ public: // If wantSub is false, return x + y, leaving sign unchanged // If wantSub is true, return abs(x - y), negating sign if x < y - static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y, - bool wantSub, ref bool sign) pure nothrow @safe if (is(Tulong == ulong)) + static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y, bool wantSub, ref bool sign) pure nothrow @safe + if (is(Tulong == ulong)) { BigUint r; if (wantSub) @@ -921,7 +924,8 @@ public: } // return x % y - static uint modInt(T)(scope BigUint x, T y_) pure if ( is(immutable T == immutable uint) ) + static uint modInt(T)(scope BigUint x, T y_) pure + if ( is(immutable T == immutable uint) ) { import core.memory : GC; uint y = y_; @@ -994,7 +998,8 @@ public: // return x op y static BigUint bitwiseOp(string op)(scope BigUint x, scope BigUint y, bool xSign, bool ySign, ref bool resultSign) - pure nothrow @safe if (op == "|" || op == "^" || op == "&") + pure nothrow @safe + if (op == "|" || op == "^" || op == "&") { auto d1 = includeSign(x.data, y.uintLength, xSign); auto d2 = includeSign(y.data, x.uintLength, ySign); diff --git a/libphobos/src/std/internal/test/dummyrange.d b/libphobos/src/std/internal/test/dummyrange.d index e07e275..42dc264 100644 --- a/libphobos/src/std/internal/test/dummyrange.d +++ b/libphobos/src/std/internal/test/dummyrange.d @@ -255,10 +255,11 @@ class ReferenceInputRange(T) { import std.array : array; - this(Range)(Range r) if (isInputRange!Range) {_payload = array(r);} - final @property ref T front(){return _payload.front;} - final void popFront(){_payload.popFront();} - final @property bool empty(){return _payload.empty;} + this(Range)(Range r) + if (isInputRange!Range) {_payload = array(r);} + final @property ref T front() {return _payload.front;} + final void popFront() {_payload.popFront();} + final @property bool empty() {return _payload.empty;} protected T[] _payload; } @@ -268,8 +269,8 @@ Infinite input range class ReferenceInfiniteInputRange(T) { this(T first = T.init) {_val = first;} - final @property T front(){return _val;} - final void popFront(){++_val;} + final @property T front() {return _val;} + final void popFront() {++_val;} enum bool empty = false; protected T _val; } @@ -279,7 +280,8 @@ Reference forward range */ class ReferenceForwardRange(T) : ReferenceInputRange!T { - this(Range)(Range r) if (isInputRange!Range) {super(r);} + this(Range)(Range r) + if (isInputRange!Range) {super(r);} final @property auto save(this This)() {return new This( _payload);} } @@ -298,9 +300,10 @@ Reference bidirectional range */ class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T { - this(Range)(Range r) if (isInputRange!Range) {super(r);} - final @property ref T back(){return _payload.back;} - final void popBack(){_payload.popBack();} + this(Range)(Range r) + if (isInputRange!Range) {super(r);} + final @property ref T back() {return _payload.back;} + final void popBack() {_payload.popBack();} } @safe unittest diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index 9dcec89..7182f6e 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -85,6 +85,7 @@ enum JSONOptions escapeNonAsciiChars = 0x2, /// Encode non-ASCII characters with a Unicode escape sequence doNotEscapeSlashes = 0x4, /// Do not escape slashes ('/') strictParsing = 0x8, /// Strictly follow RFC-8259 grammar when parsing + preserveObjectOrder = 0x16, /// Preserve order of object keys when parsing } /** @@ -126,13 +127,30 @@ struct JSONValue { import std.exception : enforce; + import std.typecons : Tuple; + + alias OrderedObjectMember = Tuple!( + string, "key", + JSONValue, "value", + ); + union Store { + struct Object + { + bool isOrdered; + union + { + JSONValue[string] unordered; + OrderedObjectMember[] ordered; + } + } + string str; long integer; ulong uinteger; double floating; - JSONValue[string] object; + Object object; JSONValue[] array; } private Store store; @@ -272,9 +290,9 @@ struct JSONValue } /*** - * Value getter/setter for `JSONType.object`. + * Value getter/setter for unordered `JSONType.object`. * Throws: `JSONException` for read access if `type` is not - * `JSONType.object`. + * `JSONType.object` or the object is ordered. * Note: This is @system because of the following pattern: --- auto a = &(json.object()); @@ -286,7 +304,9 @@ struct JSONValue { enforce!JSONException(type == JSONType.object, "JSONValue is not an object"); - return store.object; + enforce!JSONException(!store.object.isOrdered, + "JSONValue object is ordered, cannot return by ref"); + return store.object.unordered; } /// ditto @property JSONValue[string] object(return scope JSONValue[string] v) pure nothrow @nogc @trusted // TODO make @safe @@ -296,7 +316,7 @@ struct JSONValue } /*** - * Value getter for `JSONType.object`. + * Value getter for unordered `JSONType.object`. * Unlike `object`, this retrieves the object by value * and can be used in @safe code. * @@ -316,7 +336,71 @@ struct JSONValue { enforce!JSONException(type == JSONType.object, "JSONValue is not an object"); - return store.object; + if (store.object.isOrdered) + { + // Convert to unordered + JSONValue[string] result; + foreach (pair; store.object.ordered) + result[pair.key] = pair.value; + return cast(inout) result; + } + else + return store.object.unordered; + } + + /*** + * Value getter/setter for ordered `JSONType.object`. + * Throws: `JSONException` for read access if `type` is not + * `JSONType.object` or the object is unordered. + * Note: This is @system because of the following pattern: + --- + auto a = &(json.orderedObject()); + json.uinteger = 0; // overwrite AA pointer + (*a)["hello"] = "world"; // segmentation fault + --- + */ + @property ref inout(OrderedObjectMember[]) orderedObject() inout pure @system return + { + enforce!JSONException(type == JSONType.object, + "JSONValue is not an object"); + enforce!JSONException(store.object.isOrdered, + "JSONValue object is unordered, cannot return by ref"); + return store.object.ordered; + } + /// ditto + @property OrderedObjectMember[] orderedObject(return scope OrderedObjectMember[] v) pure nothrow @nogc @trusted // TODO make @safe + { + assign(v); + return v; + } + + /*** + * Value getter for ordered `JSONType.object`. + * Unlike `orderedObject`, this retrieves the object by value + * and can be used in @safe code. + */ + @property inout(OrderedObjectMember[]) orderedObjectNoRef() inout pure @trusted + { + enforce!JSONException(type == JSONType.object, + "JSONValue is not an object"); + if (store.object.isOrdered) + return store.object.ordered; + else + { + // Convert to ordered + OrderedObjectMember[] result; + foreach (key, value; store.object.unordered) + result ~= OrderedObjectMember(key, value); + return cast(inout) result; + } + } + + /// Returns `true` if the order of keys of the represented object is being preserved. + @property bool isOrdered() const pure @trusted + { + enforce!JSONException(type == JSONType.object, + "JSONValue is not an object"); + return store.object.isOrdered; } /*** @@ -517,16 +601,30 @@ struct JSONValue static if (is(Value : JSONValue)) { JSONValue[string] t = arg; - () @trusted { store.object = t; }(); + () @trusted { + store.object.isOrdered = false; + store.object.unordered = t; + }(); } else { JSONValue[string] aa; foreach (key, value; arg) aa[key] = JSONValue(value); - () @trusted { store.object = aa; }(); + () @trusted { + store.object.isOrdered = false; + store.object.unordered = aa; + }(); } } + else static if (is(T : OrderedObjectMember[])) + { + type_tag = JSONType.object; + () @trusted { + store.object.isOrdered = true; + store.object.ordered = arg; + }(); + } else static if (isArray!T) { type_tag = JSONType.array; @@ -554,7 +652,8 @@ struct JSONValue } } - private void assignRef(T)(ref T arg) if (isStaticArray!T) + private void assignRef(T)(ref T arg) + if (isStaticArray!T) { type_tag = JSONType.array; static if (is(ElementEncodingType!T : JSONValue)) @@ -582,12 +681,14 @@ struct JSONValue * and `K` i.e. a JSON object, any array or `bool`. The type will * be set accordingly. */ - this(T)(T arg) if (!isStaticArray!T) + this(T)(T arg) + if (!isStaticArray!T) { assign(arg); } /// Ditto - this(T)(ref T arg) if (isStaticArray!T) + this(T)(ref T arg) + if (isStaticArray!T) { assignRef(arg); } @@ -631,6 +732,33 @@ struct JSONValue /** * An enum value that can be used to obtain a `JSONValue` representing + * an empty JSON object. + * Unlike `emptyObject`, the order of inserted keys is preserved. + */ + enum emptyOrderedObject = { + JSONValue v = void; + v.orderedObject = null; + return v; + }(); + /// + @system unittest + { + JSONValue obj = JSONValue.emptyOrderedObject; + assert(obj.type == JSONType.object); + assert(obj.isOrdered); + obj["b"] = JSONValue(2); + obj["a"] = JSONValue(1); + assert(obj["a"] == JSONValue(1)); + assert(obj["b"] == JSONValue(2)); + + string[] keys; + foreach (string k, JSONValue v; obj) + keys ~= k; + assert(keys == ["b", "a"]); + } + + /** + * An enum value that can be used to obtain a `JSONValue` representing * an empty JSON array. */ enum emptyArray = JSONValue(JSONValue[].init); @@ -649,12 +777,14 @@ struct JSONValue assert(arr1 != arr2); } - void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue)) + void opAssign(T)(T arg) + if (!isStaticArray!T && !is(T : JSONValue)) { assign(arg); } - void opAssign(T)(ref T arg) if (isStaticArray!T) + void opAssign(T)(ref T arg) + if (isStaticArray!T) { assignRef(arg); } @@ -708,16 +838,33 @@ struct JSONValue */ void opIndexAssign(T)(auto ref T value, string key) { - enforce!JSONException(type == JSONType.object || type == JSONType.null_, - "JSONValue must be object or null"); - JSONValue[string] aa = null; - if (type == JSONType.object) + enforce!JSONException( + type == JSONType.object || + type == JSONType.null_, + "JSONValue must be object or null"); + if (type == JSONType.object && isOrdered) { - aa = this.objectNoRef; + auto arr = this.orderedObjectNoRef; + foreach (ref pair; arr) + if (pair.key == key) + { + pair.value = value; + return; + } + arr ~= OrderedObjectMember(key, JSONValue(value)); + this.orderedObject = arr; } + else + { + JSONValue[string] aa = null; + if (type == JSONType.object) + { + aa = this.objectNoRef; + } - aa[key] = value; - this.object = aa; + aa[key] = value; + this.object = aa; + } } /// @safe unittest @@ -828,6 +975,8 @@ struct JSONValue /// ditto bool opEquals(ref const JSONValue rhs) const @nogc nothrow pure @trusted { + import std.algorithm.searching : canFind; + // Default doesn't work well since store is a union. Compare only // what should be in store. // This is @trusted to remain nogc, nothrow, fast, and usable from @safe code. @@ -873,7 +1022,45 @@ struct JSONValue case JSONType.string: return type_tag == rhs.type_tag && store.str == rhs.store.str; case JSONType.object: - return type_tag == rhs.type_tag && store.object == rhs.store.object; + if (rhs.type_tag != JSONType.object) + return false; + if (store.object.isOrdered) + { + if (rhs.store.object.isOrdered) + { + if (store.object.ordered.length != rhs.store.object.ordered.length) + return false; + foreach (ref pair; store.object.ordered) + if (!rhs.store.object.ordered.canFind(pair)) + return false; + return true; + } + else + { + if (store.object.ordered.length != rhs.store.object.unordered.length) + return false; + foreach (ref pair; store.object.ordered) + if (pair.key !in rhs.store.object.unordered || + rhs.store.object.unordered[pair.key] != pair.value) + return false; + return true; + } + } + else + { + if (rhs.store.object.isOrdered) + { + if (store.object.unordered.length != rhs.store.object.ordered.length) + return false; + foreach (ref pair; rhs.store.object.ordered) + if (pair.key !in store.object.unordered || + store.object.unordered[pair.key] != pair.value) + return false; + return true; + } + else + return store.object.unordered == rhs.store.object.unordered; + } case JSONType.array: return type_tag == rhs.type_tag && store.array == rhs.store.array; case JSONType.true_: @@ -914,14 +1101,27 @@ struct JSONValue int opApply(scope int delegate(string key, ref JSONValue) dg) @system { enforce!JSONException(type == JSONType.object, - "JSONValue is not an object"); + "JSONValue is not an object"); + int result; - foreach (string key, ref value; object) + if (isOrdered) { - result = dg(key, value); - if (result) - break; + foreach (ref pair; orderedObject) + { + result = dg(pair.key, pair.value); + if (result) + break; + } + } + else + { + foreach (string key, ref value; object) + { + result = dg(key, value); + if (result) + break; + } } return result; @@ -1018,6 +1218,7 @@ if (isSomeFiniteCharInputRange!T) Nullable!Char next; int line = 1, pos = 0; immutable bool strict = (options & JSONOptions.strictParsing) != 0; + immutable bool ordered = (options & JSONOptions.preserveObjectOrder) != 0; void error(string msg) { @@ -1258,31 +1459,62 @@ if (isSomeFiniteCharInputRange!T) switch (c) { case '{': - if (testChar('}')) + if (ordered) { - value.object = null; - break; - } + if (testChar('}')) + { + value.orderedObject = null; + break; + } - JSONValue[string] obj; - do + JSONValue.OrderedObjectMember[] obj; + do + { + skipWhitespace(); + if (!strict && peekChar() == '}') + { + break; + } + checkChar('"'); + string name = parseString(); + checkChar(':'); + JSONValue member; + parseValue(member); + obj ~= JSONValue.OrderedObjectMember(name, member); + } + while (testChar(',')); + value.orderedObject = obj; + + checkChar('}'); + } + else { - skipWhitespace(); - if (!strict && peekChar() == '}') + if (testChar('}')) { + value.object = null; break; } - checkChar('"'); - string name = parseString(); - checkChar(':'); - JSONValue member; - parseValue(member); - obj[name] = member; - } - while (testChar(',')); - value.object = obj; - checkChar('}'); + JSONValue[string] obj; + do + { + skipWhitespace(); + if (!strict && peekChar() == '}') + { + break; + } + checkChar('"'); + string name = parseString(); + checkChar(':'); + JSONValue member; + parseValue(member); + obj[name] = member; + } + while (testChar(',')); + value.object = obj; + + checkChar('}'); + } break; case '[': @@ -1638,49 +1870,82 @@ if (isOutputRange!(Out,char)) final switch (value.type) { case JSONType.object: - auto obj = value.objectNoRef; - if (!obj.length) + if (value.isOrdered) { - json.put("{}"); - } - else - { - putCharAndEOL('{'); - bool first = true; - - void emit(R)(R names) + auto obj = value.orderedObjectNoRef; + if (!obj.length) { - foreach (name; names) + json.put("{}"); + } + else + { + putCharAndEOL('{'); + bool first = true; + + foreach (pair; obj) { - auto member = obj[name]; if (!first) putCharAndEOL(','); first = false; putTabs(1); - toString(name); + toString(pair.key); json.put(':'); if (pretty) json.put(' '); - toValueImpl(member, indentLevel + 1); + toValueImpl(pair.value, indentLevel + 1); } - } - import std.algorithm.sorting : sort; - // https://issues.dlang.org/show_bug.cgi?id=14439 - // auto names = obj.keys; // aa.keys can't be called in @safe code - auto names = new string[obj.length]; - size_t i = 0; - foreach (k, v; obj) + putEOL(); + putTabs(); + json.put('}'); + } + } + else + { + auto obj = value.objectNoRef; + if (!obj.length) { - names[i] = k; - i++; + json.put("{}"); } - sort(names); - emit(names); + else + { + putCharAndEOL('{'); + bool first = true; - putEOL(); - putTabs(); - json.put('}'); + void emit(R)(R names) + { + foreach (name; names) + { + auto member = obj[name]; + if (!first) + putCharAndEOL(','); + first = false; + putTabs(1); + toString(name); + json.put(':'); + if (pretty) + json.put(' '); + toValueImpl(member, indentLevel + 1); + } + } + + import std.algorithm.sorting : sort; + // https://issues.dlang.org/show_bug.cgi?id=14439 + // auto names = obj.keys; // aa.keys can't be called in @safe code + auto names = new string[obj.length]; + size_t i = 0; + foreach (k, v; obj) + { + names[i] = k; + i++; + } + sort(names); + emit(names); + + putEOL(); + putTabs(); + json.put('}'); + } } break; @@ -2469,3 +2734,17 @@ pure nothrow @safe unittest assert(app.data == s, app.data); } + +// https://issues.dlang.org/show_bug.cgi?id=24823 - JSONOptions.preserveObjectOrder +@safe unittest +{ + import std.array : appender; + + string s = `{"b":2,"a":1}`; + JSONValue j = parseJSON(s, -1, JSONOptions.preserveObjectOrder); + + auto app = appender!string(); + j.toString(app); + + assert(app.data == s, app.data); +} diff --git a/libphobos/src/std/logger/core.d b/libphobos/src/std/logger/core.d index 0633bdd..cc938d4 100644 --- a/libphobos/src/std/logger/core.d +++ b/libphobos/src/std/logger/core.d @@ -293,7 +293,7 @@ template defaultLogFunction(LogLevel ll) string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) + if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) { stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, prettyFuncName, moduleName)(args); @@ -446,7 +446,7 @@ private struct MsgRange } void put(T)(T msg) @safe - if (isSomeString!T) + if (isSomeString!T) { log.logMsgPart(msg); } @@ -735,7 +735,7 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) + if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) { synchronized (mutex) { @@ -948,7 +948,7 @@ abstract class Logger string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, lazy bool condition, lazy A args) - if (args.length != 1) + if (args.length != 1) { synchronized (mutex) { @@ -1016,7 +1016,7 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) - if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) + if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) { synchronized (mutex) { @@ -1085,7 +1085,7 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) - if (args.length != 1) + if (args.length != 1) { synchronized (mutex) { @@ -1154,10 +1154,10 @@ abstract class Logger string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__, A...)(lazy A args) - if ((args.length > 1 - && !is(Unqual!(A[0]) : bool) - && !is(immutable A[0] == immutable LogLevel)) - || args.length == 0) + if ((args.length > 1 + && !is(Unqual!(A[0]) : bool) + && !is(immutable A[0] == immutable LogLevel)) + || args.length == 0) { synchronized (mutex) { diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d index 9f0fb56..3fef8e4 100644 --- a/libphobos/src/std/numeric.d +++ b/libphobos/src/std/numeric.d @@ -496,7 +496,8 @@ public: static @property CustomFloat im() { return CustomFloat(0.0f); } /// Initialize from any `real` compatible type. - this(F)(F input) if (__traits(compiles, cast(real) input )) + this(F)(F input) + if (__traits(compiles, cast(real) input )) { this = input; } @@ -512,7 +513,7 @@ public: /// Assigns from any `real` compatible type. void opAssign(F)(F input) - if (__traits(compiles, cast(real) input)) + if (__traits(compiles, cast(real) input)) { import std.conv : text; @@ -546,7 +547,7 @@ public: /// Fetches the stored value either as a `float`, `double` or `real`. @property F get(F)() - if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0) + if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0) { import std.conv : text; @@ -574,7 +575,7 @@ public: /// Convert the CustomFloat to a real and perform the relevant operator on the result real opUnary(string op)() - if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--") + if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--") { static if (op=="++" || op=="--") { @@ -591,31 +592,31 @@ public: // do not match equally, which is disallowed by the spec: // https://dlang.org/spec/operatoroverloading.html#binary real opBinary(string op,T)(T b) - if (__traits(compiles, mixin(`get!real`~op~`b.get!real`))) - { - return mixin(`get!real`~op~`b.get!real`); - } + if (__traits(compiles, mixin(`get!real`~op~`b.get!real`))) + { + return mixin(`get!real`~op~`b.get!real`); + } /// ditto real opBinary(string op,T)(T b) - if ( __traits(compiles, mixin(`get!real`~op~`b`)) && - !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) + if ( __traits(compiles, mixin(`get!real`~op~`b`)) && + !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) { return mixin(`get!real`~op~`b`); } /// ditto real opBinaryRight(string op,T)(T a) - if ( __traits(compiles, mixin(`a`~op~`get!real`)) && - !__traits(compiles, mixin(`get!real`~op~`b`)) && - !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) + if ( __traits(compiles, mixin(`a`~op~`get!real`)) && + !__traits(compiles, mixin(`get!real`~op~`b`)) && + !__traits(compiles, mixin(`get!real`~op~`b.get!real`))) { return mixin(`a`~op~`get!real`); } /// ditto int opCmp(T)(auto ref T b) - if (__traits(compiles, cast(real) b)) + if (__traits(compiles, cast(real) b)) { auto x = get!real; auto y = cast(real) b; @@ -624,7 +625,7 @@ public: /// ditto void opOpAssign(string op, T)(auto ref T b) - if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`))) + if (__traits(compiles, mixin(`get!real`~op~`cast(real) b`))) { return mixin(`this = this `~op~` cast(real) b`); } @@ -3687,7 +3688,7 @@ public: * i.e., output[j] := sum[ exp(-2 PI i j k / N) input[k] ]. */ Complex!F[] fft(F = double, R)(R range) const - if (isFloatingPoint!F && isRandomAccessRange!R) + if (isFloatingPoint!F && isRandomAccessRange!R) { enforceSize(range); Complex!F[] ret; @@ -3710,7 +3711,7 @@ public: * property that can be both read and written and are floating point numbers. */ void fft(Ret, R)(R range, Ret buf) const - if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) + if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) { assert(buf.length == range.length); enforceSize(range); @@ -3759,7 +3760,7 @@ public: * output[j] := (1 / N) sum[ exp(+2 PI i j k / N) input[k] ]. */ Complex!F[] inverseFft(F = double, R)(R range) const - if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F) + if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F) { enforceSize(range); Complex!F[] ret; @@ -3781,7 +3782,7 @@ public: * must be some complex-like type. */ void inverseFft(Ret, R)(R range, Ret buf) const - if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) + if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret) { enforceSize(range); diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d index 7525d9b..bafdbb9 100644 --- a/libphobos/src/std/parallelism.d +++ b/libphobos/src/std/parallelism.d @@ -2269,7 +2269,8 @@ public: call to `popFront` or, if thrown during construction, simply allowed to propagate to the caller. */ - auto asyncBuf(S)(S source, size_t bufSize = 100) if (isInputRange!S) + auto asyncBuf(S)(S source, size_t bufSize = 100) + if (isInputRange!S) { static final class AsyncBuf { diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d index a45865a..9dae2a6 100644 --- a/libphobos/src/std/path.d +++ b/libphobos/src/std/path.d @@ -1446,9 +1446,8 @@ private auto _withDefaultExtension(R, C)(R path, C[] ext) of segments to assemble the path from. Returns: The assembled path. */ -immutable(ElementEncodingType!(ElementType!Range))[] - buildPath(Range)(scope Range segments) - if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range)) +immutable(ElementEncodingType!(ElementType!Range))[] buildPath(Range)(scope Range segments) +if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range)) { if (segments.empty) return null; @@ -3396,7 +3395,10 @@ do } else { + import core.memory : pureMalloc, pureFree; C[] pattmp; + scope(exit) if (pattmp !is null) (() @trusted => pureFree(pattmp.ptr))(); + for (size_t pi = 0; pi < pattern.length; pi++) { const pc = pattern[pi]; @@ -3482,9 +3484,12 @@ do * pattern[pi0 .. pi-1] ~ pattern[piRemain..$] */ if (pattmp is null) + { // Allocate this only once per function invocation. - // Should do it with malloc/free, but that would make it impure. - pattmp = new C[pattern.length]; + pattmp = (() @trusted => + (cast(C*) pureMalloc(C.sizeof * pattern.length))[0 .. pattern.length]) + (); + } const len1 = pi - 1 - pi0; pattmp[0 .. len1] = pattern[pi0 .. pi - 1]; @@ -3518,7 +3523,7 @@ do } /// -@safe unittest +@safe @nogc unittest { assert(globMatch("foo.bar", "*")); assert(globMatch("foo.bar", "*.*")); diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index 41f788d..4f593bd 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -1050,54 +1050,103 @@ private Pid spawnProcessPosix(scope const(char[])[] args, static if (!__traits(compiles, closefrom)) { - // FIXME: This implementation crashes the system when RLIMIT_NOFILE - // has a big value. For a possible solution see: - // https://github.com/dlang/phobos/pull/8990 - void fallback (int lowfd) { - // NOTE: malloc() and getrlimit() are not on the POSIX async - // signal safe functions list, but practically this should - // not be a problem. Java VM and CPython also use malloc() - // in its own implementation via opendir(). - import core.stdc.stdlib : malloc; - import core.sys.posix.poll : pollfd, poll, POLLNVAL; + void fallback (int lowfd) + { + import core.sys.posix.dirent : dirent, opendir, readdir, closedir, DIR; + import core.sys.posix.unistd : close; + import core.sys.posix.stdlib : atoi, malloc, free; import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; // Get the maximum number of file descriptors that could be open. rlimit r; if (getrlimit(RLIMIT_NOFILE, &r) != 0) - { abortOnError(forkPipeOut, InternalError.getrlimit, .errno); - } + immutable maxDescriptors = cast(int) r.rlim_cur; - immutable maxToClose = maxDescriptors - lowfd; + // Missing druntime declaration + pragma(mangle, "dirfd") + extern(C) nothrow @nogc int dirfd(DIR* dir); - // Call poll() to see which ones are actually open: - auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose); - if (pfds is null) - { - abortOnError(forkPipeOut, InternalError.malloc, .errno); - } - foreach (i; 0 .. maxToClose) + DIR* dir = null; + + // We read from /dev/fd or /proc/self/fd only if the limit is high enough + if (maxDescriptors > 128*1024) { - pfds[i].fd = i + lowfd; - pfds[i].events = 0; - pfds[i].revents = 0; + // Try to open the directory /dev/fd or /proc/self/fd + dir = opendir("/dev/fd"); + if (dir is null) dir = opendir("/proc/self/fd"); } - if (poll(pfds, maxToClose, 0) >= 0) + + // If we have a directory, close all file descriptors except stdin, stdout, and stderr + if (dir) { - foreach (i; 0 .. maxToClose) + scope(exit) closedir(dir); + + int opendirfd = dirfd(dir); + + // Iterate over all file descriptors + while (true) { - // POLLNVAL will be set if the file descriptor is invalid. - if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); + dirent* entry = readdir(dir); + + if (entry is null) break; + if (entry.d_name[0] == '.') continue; + + int fd = atoi(cast(char*) entry.d_name); + + // Don't close stdin, stdout, stderr, or the directory file descriptor + if (fd < lowfd || fd == opendirfd) continue; + + close(fd); } } else { - // Fall back to closing everything. - foreach (i; lowfd .. maxDescriptors) + // This is going to allocate 8 bytes for each possible file descriptor from lowfd to r.rlim_cur + if (maxDescriptors <= 128*1024) { - close(i); + // NOTE: malloc() and getrlimit() are not on the POSIX async + // signal safe functions list, but practically this should + // not be a problem. Java VM and CPython also use malloc() + // in its own implementation via opendir(). + import core.stdc.stdlib : malloc; + import core.sys.posix.poll : pollfd, poll, POLLNVAL; + + immutable maxToClose = maxDescriptors - lowfd; + + // Call poll() to see which ones are actually open: + auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose); + if (pfds is null) + { + abortOnError(forkPipeOut, InternalError.malloc, .errno); + } + + foreach (i; 0 .. maxToClose) + { + pfds[i].fd = i + lowfd; + pfds[i].events = 0; + pfds[i].revents = 0; + } + + if (poll(pfds, maxToClose, 0) < 0) + // couldn't use poll, use the slow path. + goto LslowClose; + + foreach (i; 0 .. maxToClose) + { + // POLLNVAL will be set if the file descriptor is invalid. + if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); + } + } + else + { + LslowClose: + // Fall back to closing everything. + foreach (i; lowfd .. maxDescriptors) + { + close(i); + } } } } @@ -1106,6 +1155,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args, // Until we find a way to perform this check we will try to use dlsym to // check for the function. See: https://github.com/dlang/phobos/pull/9048 version (CRuntime_Glibc) + { void closefrom (int lowfd) { static bool tryGlibcClosefrom (int lowfd) { import core.sys.posix.dlfcn : dlopen, dlclose, dlsym, dlerror, RTLD_LAZY; @@ -1129,6 +1179,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args, if (!tryGlibcClosefrom(lowfd)) fallback(lowfd); } + } else alias closefrom = fallback; } diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index 87e63f3..c221024 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -935,7 +935,8 @@ Parameters for the generator. `Exception` if the InputRange didn't provide enough elements to seed the generator. The number of elements required is the 'n' template parameter of the MersenneTwisterEngine struct. */ - void seed(T)(T range) if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) + void seed(T)(T range) + if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) { this.seedImpl(range, this.state); } @@ -945,7 +946,7 @@ Parameters for the generator. which can be used with an arbitrary `State` instance */ private static void seedImpl(T)(T range, ref State mtState) - if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) + if (isInputRange!T && is(immutable ElementType!T == immutable UIntType)) { size_t j; for (j = 0; j < n && !range.empty; ++j, range.popFront()) @@ -2215,6 +2216,7 @@ at least that number won't be represented fairly. Hence, our condition to reroll is `bucketFront > (UpperType.max - (upperDist - 1))` +/ +/// ditto auto uniform(string boundaries = "[)", T1, T2, RandomGen) (T1 a, T2 b, ref RandomGen rng) if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && @@ -2277,9 +2279,14 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && return cast(ResultType)(lower + offset); } +/// @safe unittest { import std.conv : to; + import std.meta : AliasSeq; + import std.range.primitives : isForwardRange; + import std.traits : isIntegral, isSomeChar; + auto gen = Mt19937(123_456_789); static assert(isForwardRange!(typeof(gen))); @@ -2290,7 +2297,7 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && auto c = uniform(0.0, 1.0); assert(0 <= c && c < 1); - static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, + static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real)) {{ T lo = 0, hi = 100; @@ -2344,7 +2351,7 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && auto reproRng = Xorshift(239842); - static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, + static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong)) {{ T lo = T.min + 10, hi = T.max - 10; diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index e2a2d7d..3a135eb 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -6095,8 +6095,10 @@ private struct LockstepMixin(Ranges...) { indexDef = q{ size_t index = ranges[0].length - 1; - enforce(this.stoppingPolicy == StoppingPolicy.requireSameLength, - "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); + enforce( + this.stoppingPolicy == StoppingPolicy.requireSameLength, + "Indexed lockstep can only be used with foreach_reverse when " ~ + "stoppingPolicy == requireSameLength"); foreach (range; ranges[1 .. $]) enforce(range.length == ranges[0].length); @@ -7490,7 +7492,8 @@ if (!isIntegral!(CommonType!(B, E)) && bool opEquals(Cyclic c) const { return current == c.current; } bool opEquals(int i) const { return current == i; } - void opUnary(string op)() if (op == "++") + void opUnary(string op)() + if (op == "++") { current = (current + 1) % wrapAround; } @@ -12444,7 +12447,7 @@ public: return (*_range).front; } - static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const + static if (is(typeof(((const R* r) => (*r).front)(null)))) @property auto front() const { return (*_range).front; } @@ -12470,7 +12473,7 @@ public: return (*_range).empty; } - static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const + static if (is(typeof(((const R* r) => (*r).empty)(null)))) @property bool empty() const { return (*_range).empty; } @@ -12500,10 +12503,11 @@ public: else static if (isForwardRange!R) { import std.traits : isSafe; - private alias S = typeof((*_range).save); + private alias S = typeof((() => (*_range).save)()); + + static if (is(typeof(((const R* r) => (*r).save)(null)))) + private alias CS = typeof(((const R* r) => (*r).save)(null)); - static if (is(typeof((*cast(const R*)_range).save))) - private alias CS = typeof((*cast(const R*)_range).save); static if (isSafe!((R* r) => (*r).save)) { @@ -12512,7 +12516,7 @@ public: mixin(_genSave()); } - static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const + static if (is(typeof(((const R* r) => (*r).save)(null)))) @property RefRange!CS save() @trusted const { mixin(_genSave()); } @@ -12524,7 +12528,7 @@ public: mixin(_genSave()); } - static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const + static if (is(typeof(((const R* r) => (*r).save)(null)))) @property RefRange!CS save() const { mixin(_genSave()); } @@ -12543,7 +12547,7 @@ public: private static string _genSave() @safe pure nothrow { return `import core.lifetime : emplace;` ~ - `alias S = typeof((*_range).save);` ~ + `alias S = typeof((() => (*_range).save)());` ~ `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~ `auto mem = new void[S.sizeof];` ~ `emplace!S(mem, cast(S)(*_range).save);` ~ @@ -12572,7 +12576,7 @@ public: return (*_range).back; } - static if (is(typeof((*(cast(const R*)_range)).back))) @property auto back() const + static if (is(typeof(((const R* r) => (*r).back)(null)))) @property auto back() const { return (*_range).back; } @@ -12604,13 +12608,13 @@ public: else static if (isRandomAccessRange!R) { auto ref opIndex(IndexType)(IndexType index) - if (is(typeof((*_range)[index]))) + if (is(typeof((*_range)[index]))) { return (*_range)[index]; } auto ref opIndex(IndexType)(IndexType index) const - if (is(typeof((*cast(const R*)_range)[index]))) + if (is(typeof((*cast(const R*)_range)[index]))) { return (*_range)[index]; } @@ -12662,7 +12666,7 @@ public: { return (*_range).length; } - static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const + static if (is(typeof(((const R* r) => (*r).length)(null)))) @property auto length() const { return (*_range).length; } @@ -12692,14 +12696,14 @@ public: RefRange!T opSlice(IndexType1, IndexType2) (IndexType1 begin, IndexType2 end) - if (is(typeof((*_range)[begin .. end]))) + if (is(typeof((*_range)[begin .. end]))) { mixin(_genOpSlice()); } RefRange!CT opSlice(IndexType1, IndexType2) (IndexType1 begin, IndexType2 end) const - if (is(typeof((*cast(const R*)_range)[begin .. end]))) + if (is(typeof((*cast(const R*)_range)[begin .. end]))) { mixin(_genOpSlice()); } @@ -13105,6 +13109,44 @@ private: auto rr2 = refRange(&r2); } +// https://issues.dlang.org/show_bug.cgi?id=24801 +@safe unittest +{ + + { + static struct R + { + int front() => 0; + void popFront() {} + bool empty() => false; + } + R range; + auto r = RefRange!R(&range); + } + + { + static struct R + { + size_t start, end; + size_t length() => end - start; + int opIndex(size_t i) => 0; + + + int front() => this[0]; + int back() => this[length-1]; + void popFront() { start++; } + void popBack() { end--; } + bool empty() => length == 0; + R save() const => R(); + } + + R range; + auto r = RefRange!R(&range); + } + + +} + /// ditto auto refRange(R)(R* range) if (isInputRange!R) diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d index dddcae9..8436625 100644 --- a/libphobos/src/std/range/primitives.d +++ b/libphobos/src/std/range/primitives.d @@ -474,7 +474,8 @@ void put(R, E)(ref R r, E e) { string data; - void put(C)(C c) if (isSomeChar!C) + void put(C)(C c) + if (isSomeChar!C) { data ~= c; } diff --git a/libphobos/src/std/regex/internal/backtracking.d b/libphobos/src/std/regex/internal/backtracking.d index a488e06..605ec03 100644 --- a/libphobos/src/std/regex/internal/backtracking.d +++ b/libphobos/src/std/regex/internal/backtracking.d @@ -702,7 +702,7 @@ final: } void stackPush(T)(T val) - if (!isDynamicArray!T) + if (!isDynamicArray!T) { *cast(T*)&memory[lastState] = val; enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; @@ -720,7 +720,7 @@ final: } void stackPop(T)(ref T val) - if (!isDynamicArray!T) + if (!isDynamicArray!T) { enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof; lastState -= delta; diff --git a/libphobos/src/std/regex/internal/parser.d b/libphobos/src/std/regex/internal/parser.d index ab2b297..d8ebe59 100644 --- a/libphobos/src/std/regex/internal/parser.d +++ b/libphobos/src/std/regex/internal/parser.d @@ -542,7 +542,7 @@ if (isForwardRange!R && is(ElementType!R : dchar)) Generator g; @trusted this(S)(R pattern, S flags) - if (isSomeString!S) + if (isSomeString!S) { pat = origin = pattern; //reserve slightly more then avg as sampled from unittests diff --git a/libphobos/src/std/regex/internal/thompson.d b/libphobos/src/std/regex/internal/thompson.d index f4643ae..195625f 100644 --- a/libphobos/src/std/regex/internal/thompson.d +++ b/libphobos/src/std/regex/internal/thompson.d @@ -276,7 +276,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.RepeatEnd || code == IR.RepeatQEnd) + if (code == IR.RepeatEnd || code == IR.RepeatQEnd) { with(e) with(state) { @@ -331,7 +331,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd) + if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd) { with(e) with(state) { @@ -366,7 +366,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.InfiniteBloomEnd) + if (code == IR.InfiniteBloomEnd) { with(e) with(state) { @@ -507,7 +507,7 @@ template ThompsonOps(E, S, bool withInput:true) static bool op(IR code)(E e, S* state) - if (code == IR.LookbehindStart || code == IR.NeglookbehindStart) + if (code == IR.LookbehindStart || code == IR.NeglookbehindStart) { with(e) with(state) { @@ -534,7 +534,7 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.LookaheadStart || code == IR.NeglookaheadStart) + if (code == IR.LookaheadStart || code == IR.NeglookaheadStart) { with(e) with(state) { @@ -563,8 +563,8 @@ template ThompsonOps(E, S, bool withInput:true) } static bool op(IR code)(E e, S* state) - if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd || - code == IR.LookbehindEnd || code == IR.NeglookbehindEnd) + if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd || + code == IR.LookbehindEnd || code == IR.NeglookbehindEnd) { with(e) with(state) { @@ -675,7 +675,7 @@ template ThompsonOps(E,S, bool withInput:false) @trusted: // can't match these without input static bool op(IR code)(E e, S* state) - if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet + if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet || code == IR.Trie || code == IR.Char || code == IR.Any) { return state.popState(e); @@ -701,7 +701,7 @@ template ThompsonOps(E,S, bool withInput:false) // forward all control flow to normal versions static bool op(IR code)(E e, S* state) - if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet + if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet && code != IR.Trie && code != IR.Char && code != IR.Any && code != IR.Backref) { return ThompsonOps!(E,S,true).op!code(e,state); diff --git a/libphobos/src/std/regex/package.d b/libphobos/src/std/regex/package.d index d6a01e2..143b683 100644 --- a/libphobos/src/std/regex/package.d +++ b/libphobos/src/std/regex/package.d @@ -688,7 +688,7 @@ public: ---- +/ R opIndex(String)(String i) /*const*/ //@@@BUG@@@ - if (isSomeString!String) + if (isSomeString!String) { size_t index = lookupNamedGroup(_names, i); return getMatch(index); diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d index e86a51f..52fd33b 100644 --- a/libphobos/src/std/socket.d +++ b/libphobos/src/std/socket.d @@ -702,7 +702,7 @@ class InternetHost // must synchronize across all threads private bool getHost(string opMixin, T)(T param) @system { - synchronized(this.classinfo) + synchronized(typeid(this)) return getHostNoSync!(opMixin, T)(param); } } diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index b474460..e844297 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -447,7 +447,7 @@ Throws: `ErrnoException` if the file could not be opened. /// ditto this(R1, R2)(R1 name) - if (isSomeFiniteCharInputRange!R1) + if (isSomeFiniteCharInputRange!R1) { import std.conv : to; this(name.to!string, "rb"); @@ -455,8 +455,8 @@ Throws: `ErrnoException` if the file could not be opened. /// ditto this(R1, R2)(R1 name, R2 mode) - if (isSomeFiniteCharInputRange!R1 && - isSomeFiniteCharInputRange!R2) + if (isSomeFiniteCharInputRange!R1 && + isSomeFiniteCharInputRange!R2) { import std.conv : to; this(name.to!string, mode.to!string); @@ -2340,6 +2340,24 @@ void main() Notice that neither example accesses the line data returned by `front` after the corresponding `popFront` call is made (because the contents may well have changed). +---- + +Windows specific Example: +---- +import std.stdio; + +version (Windows) +void main() +{ + + foreach (line; File("file.txt").byLine(No.keepTerminator, "\r\n")) + { + writeln("|"~line~"|"); + if (line == "HelloWorld") + writeln("^This Line is here."); + } + +} */ auto byLine(Terminator = char, Char = char) (KeepTerminator keepTerminator = No.keepTerminator, @@ -2997,10 +3015,10 @@ is empty, throws an `Exception`. In case of an I/O error throws /// Range primitive implementations. void put(A)(scope A writeme) - if ((isSomeChar!(ElementType!A) || - is(ElementType!A : const(ubyte))) && - isInputRange!A && - !isInfinite!A) + if ((isSomeChar!(ElementType!A) || + is(ElementType!A : const(ubyte))) && + isInputRange!A && + !isInfinite!A) { import std.exception : errnoEnforce; @@ -3026,7 +3044,8 @@ is empty, throws an `Exception`. In case of an I/O error throws } /// ditto - void put(C)(scope C c) @safe if (isSomeChar!C || is(C : const(ubyte))) + void put(C)(scope C c) @safe + if (isSomeChar!C || is(C : const(ubyte))) { import std.utf : decodeFront, encode, stride; diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d index 21e1ca3..bcc9d7c 100644 --- a/libphobos/src/std/string.d +++ b/libphobos/src/std/string.d @@ -457,26 +457,26 @@ pure nothrow @system unittest // https://issues.dlang.org/show_bug.cgi?id=15136 alias CaseSensitive = Flag!"caseSensitive"; /++ - Searches for character in range. + Searches for a character in a string or range. Params: - s = string or InputRange of characters to search in correct UTF format - c = character to search for - startIdx = starting index to a well-formed code point - cs = `Yes.caseSensitive` or `No.caseSensitive` + s = string or InputRange of characters to search for `c` in + c = character to search for in `s` + startIdx = index to a well-formed code point in `s` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`). Returns: - the index of the first occurrence of `c` in `s` with - respect to the start index `startIdx`. If `c` - is not found, then `-1` is returned. - If `c` is found the value of the returned index is at least - `startIdx`. - If the parameters are not valid UTF, the result will still - be in the range [-1 .. s.length], but will not be reliable otherwise. + If `c` is found in `s`, then the index of its first occurrence is + returned. If `c` is not found or `startIdx` is greater than or equal to + `s.length`, then -1 is returned. If the parameters are not valid UTF, + the result will still be either -1 or in the range [`startIdx` .. + `s.length`], but will not be reliable otherwise. Throws: - If the sequence starting at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. See_Also: $(REF countUntil, std,algorithm,searching) +/ @@ -901,30 +901,30 @@ private template _indexOfStr(CaseSensitive cs) } /++ - Searches for substring in `s`. + Searches for a substring in a string or range. Params: - s = string or ForwardRange of characters to search in correct UTF format - sub = substring to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` (default) or `No.caseSensitive` + s = string or ForwardRange of characters to search for `sub` in + sub = substring to search for in `s` + startIdx = index to a well-formed code point in `s` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) Returns: - the index of the first occurrence of `sub` in `s` with - respect to the start index `startIdx`. If `sub` is not found, - then `-1` is returned. - If the arguments are not valid UTF, the result will still - be in the range [-1 .. s.length], but will not be reliable otherwise. - If `sub` is found the value of the returned index is at least - `startIdx`. + The index of the first occurrence of `sub` in `s`. If `sub` is not found + or `startIdx` is greater than or equal to `s.length`, then -1 is + returned. If the arguments are not valid UTF, the result will still be + either -1 or in the range [`startIdx` .. `s.length`], but will not be + reliable otherwise. Throws: - If the sequence starting at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. Bugs: - Does not work with case insensitive strings where the mapping of - tolower and toupper is not 1:1. + Does not work with case-insensitive strings where the mapping of + $(REF toLower, std,uni) and $(REF toUpper, std,uni) is not 1:1. +/ ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub) if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && @@ -1156,23 +1156,26 @@ unittest } /++ + Searches for the last occurrence of a character in a string. + Params: - s = string to search - c = character to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` or `No.caseSensitive` + s = string to search for `c` in + c = character to search for in `s` + startIdx = index of a well-formed code point in `s` to start searching + from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) Returns: - The index of the last occurrence of `c` in `s`. If `c` is not - found, then `-1` is returned. The `startIdx` slices `s` in - the following way $(D s[0 .. startIdx]). `startIdx` represents a - codeunit index in `s`. + If `c` is found in `s`, then the index of its last occurrence is + returned. If `c` is not found or `startIdx` is greater than or equal to + `s.length`, then -1 is returned. If the parameters are not valid UTF, + the result will still be either -1 or in the range [`startIdx` .. + `s.length`], but will not be reliable otherwise. Throws: - If the sequence ending at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. - - `cs` indicates whether the comparisons are case sensitive. + If the sequence ending at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. +/ ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c, in CaseSensitive cs = Yes.caseSensitive) @safe pure @@ -1345,23 +1348,30 @@ if (isSomeChar!Char) } /++ + Searches for the last occurrence of a substring in a string. + Params: - s = string to search - sub = substring to search for - startIdx = the index into s to start searching from - cs = `Yes.caseSensitive` or `No.caseSensitive` + s = string to search for `sub` in + sub = substring to search for in `s` + startIdx = index to a well-formed code point in `s` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) Returns: - the index of the last occurrence of `sub` in `s`. If `sub` is - not found, then `-1` is returned. The `startIdx` slices `s` - in the following way $(D s[0 .. startIdx]). `startIdx` represents a - codeunit index in `s`. + The index of the last occurrence of `sub` in `s`. If `sub` is not found + or `startIdx` is greater than or equal to `s.length`, then -1 is + returned. If the parameters are not valid UTF, the result will still be + either -1 or in the range [`startIdx` .. `s.length`], but will not be + reliable otherwise. Throws: - If the sequence ending at `startIdx` does not represent a well - formed codepoint, then a $(REF UTFException, std,utf) may be thrown. + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. - `cs` indicates whether the comparisons are case sensitive. + Bugs: + Does not work with case-insensitive strings where the mapping of + $(REF toLower, std,uni) and $(REF toUpper, std,uni) is not 1:1. +/ ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub, in CaseSensitive cs = Yes.caseSensitive) @safe pure @@ -1747,21 +1757,28 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the first occurrence of any of the elements in $(D - needles) in `haystack`. If no element of `needles` is found, - then `-1` is returned. The `startIdx` slices `haystack` in the - following way $(D haystack[startIdx .. $]). `startIdx` represents a - codeunit index in `haystack`. If the sequence ending at `startIdx` - does not represent a well formed codepoint, then a $(REF UTFException, std,utf) - may be thrown. + Searches the string `haystack` for one of the characters in `needles` + starting at index `startIdx`. If `startIdx` is not given, it defaults to 0. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search for needles in + needles = characters to search for in `haystack` + startIdx = index of a well-formed code point in `haystack` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the first occurrence of any of the elements of `needles` in + `haystack`. If no element of `needles` is found or `startIdx` is greater + than or equal to `haystack.length`, then -1 is returned. If the + parameters are not valid UTF, the result will still be either -1 or in + the range [`startIdx` .. `haystack.length`], but will not be reliable + otherwise. + + Throws: + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. */ ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @safe pure @@ -1914,21 +1931,23 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the last occurrence of any of the elements in $(D - needles) in `haystack`. If no element of `needles` is found, - then `-1` is returned. The `stopIdx` slices `haystack` in the - following way $(D s[0 .. stopIdx]). `stopIdx` represents a codeunit - index in `haystack`. If the sequence ending at `startIdx` does not - represent a well formed codepoint, then a $(REF UTFException, std,utf) may be - thrown. + Searches `haystack` for the last occurrence of any of the + characters in `needles`. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If - the stopIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search needles in + needles = characters to search for in `haystack` + stopIdx = index in `haystack` to stop searching at (exclusive); defaults + to `haystack.length` + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the last occurrence of any of the characters of `needles` + in `haystack`. If no character of `needles` is found or `stopIdx` is 0, + then -1 is returned. If the parameters are not valid UTF, the result + will still be in the range [-1 .. `stopIdx`], but will not be reliable + otherwise. */ ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @@ -2097,17 +2116,27 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the first occurrence of any character not an elements - in `needles` in `haystack`. If all element of `haystack` are - element of `needles` `-1` is returned. + Searches `haystack` for a character not in `needles`. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - startIdx = slices haystack like this $(D haystack[startIdx .. $]). If - the startIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search for needles in + needles = characters to search for in `haystack` + startIdx = index of a well-formed code point in `haystack` to start + searching from; defaults to 0 + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the first character in `haystack` that is not an element of + `needles`. If all characters of `haystack` are elements of `needles` or + `startIdx` is greater than or equal to `haystack.length`, then -1 is + returned. If the parameters are not valid UTF, the result will still be + either -1 or in the range [`startIdx` .. `haystack.length`], but will + not be reliable otherwise. + + Throws: + If the sequence starting at `startIdx` does not represent a well-formed + code point, then a $(REF UTFException, std,utf) may be thrown. */ ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) @@ -2257,17 +2286,22 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the last index of the first occurence of any character that is not - an elements in `needles` in `haystack`. If all element of - `haystack` are element of `needles` `-1` is returned. + Searches for the last character in `haystack` that is not in `needles`. Params: - haystack = String to search for needles in. - needles = Strings to search for in haystack. - stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If - the stopIdx is greater than or equal to the length of haystack the - functions returns `-1`. - cs = Indicates whether the comparisons are case sensitive. + haystack = string to search for needles in + needles = characters to search for in `haystack` + stopIdx = index in `haystack` to stop searching at (exclusive); + defaults to `haystack.length` + cs = specifies whether comparisons are case-sensitive + (`Yes.caseSensitive`) or not (`No.caseSensitive`) + + Returns: + The index of the last character in `haystack` that is not an element of + `needles`. If all characters of `haystack` are in `needles` or `stopIdx` + is 0, then -1 is returned. If the parameters are not valid UTF, the + result will still be in the range [-1 .. `stopIdx`], but will not be + reliable otherwise. */ ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, in CaseSensitive cs = Yes.caseSensitive) diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index 5ed37a1..69362c0 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -7367,10 +7367,12 @@ template isInstanceOf(alias S, alias T) static struct A(T = void) { // doesn't work as expected, only accepts A when T = void - void func(B)(B b) if (isInstanceOf!(A, B)) {} + void func(B)(B b) + if (isInstanceOf!(A, B)) {} // correct behavior - void method(B)(B b) if (isInstanceOf!(TemplateOf!(A), B)) {} + void method(B)(B b) + if (isInstanceOf!(TemplateOf!(A), B)) {} } A!(void) a1; diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index aceb287..c874c0f 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -2235,12 +2235,14 @@ template tuple(Names...) // e.g. Tuple!(int, "x", string, "y") template Interleave(A...) { - template and(B...) if (B.length == 1) + template and(B...) + if (B.length == 1) { alias and = AliasSeq!(A[0], B[0]); } - template and(B...) if (B.length != 1) + template and(B...) + if (B.length != 1) { alias and = AliasSeq!(A[0], B[0], Interleave!(A[1..$]).and!(B[1..$])); @@ -5134,7 +5136,7 @@ Params: non-release mode. */ void opAssign()(T value) - if (isAssignable!T) //@@@9416@@@ + if (isAssignable!T) //@@@9416@@@ { enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ "."; assert(!isNull, message); @@ -5469,15 +5471,17 @@ nothrow pure @safe unittest } } -// / ditto +/// ditto class NotImplementedError : Error { + /// this(string method) nothrow pure @safe { super(method ~ " is not implemented"); } } +/// @system unittest { import std.exception : assertThrown; @@ -7498,7 +7502,8 @@ Constructor that initializes the payload. Postcondition: `refCountedStore.isInitialized` */ - this(A...)(auto ref A args) if (A.length > 0) + this(A...)(auto ref A args) + if (A.length > 0) out { assert(refCountedStore.isInitialized); @@ -7931,7 +7936,8 @@ template borrow(alias fun) { import std.functional : unaryFun; - auto ref borrow(RC)(RC refCount) if + auto ref borrow(RC)(RC refCount) + if ( isInstanceOf!(SafeRefCounted, RC) && is(typeof(unaryFun!fun(refCount.refCountedPayload))) @@ -8140,7 +8146,7 @@ mixin template Proxy(alias a) } bool opEquals(T)(T b) - if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a)))) + if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a)))) { static if (is(typeof(a.opEquals(b)))) return a.opEquals(b); @@ -8164,7 +8170,7 @@ mixin template Proxy(alias a) } int opCmp(T)(auto ref const T b) - if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a)))) + if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a)))) { static if (is(typeof(a.opCmp(b)))) return a.opCmp(b); @@ -8274,7 +8280,8 @@ mixin template Proxy(alias a) } } - auto ref opAssign (this X, V )(auto ref V v) if (!is(V == typeof(this))) { return a = v; } + auto ref opAssign (this X, V )(auto ref V v) + if (!is(V == typeof(this))) { return a = v; } auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; } auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; } auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b .. e] = v; } @@ -9793,6 +9800,7 @@ Flag!"encryption".no). */ struct Yes { + /// template opDispatch(string name) { enum opDispatch = Flag!name.yes; @@ -9803,6 +9811,7 @@ struct Yes /// Ditto struct No { + /// template opDispatch(string name) { enum opDispatch = Flag!name.no; @@ -9941,7 +9950,7 @@ public: } this(T...)(T flags) - if (allSatisfy!(isBaseEnumType, T)) + if (allSatisfy!(isBaseEnumType, T)) { this = flags; } @@ -9952,19 +9961,19 @@ public: } Base opCast(B)() const - if (is(Base : B)) + if (is(Base : B)) { return mValue; } auto opUnary(string op)() const - if (op == "~") + if (op == "~") { return BitFlags(cast(E) cast(Base) ~mValue); } auto ref opAssign(T...)(T flags) - if (allSatisfy!(isBaseEnumType, T)) + if (allSatisfy!(isBaseEnumType, T)) { mValue = 0; foreach (E flag; flags) @@ -10005,7 +10014,7 @@ public: } auto opBinary(string op)(BitFlags flags) const - if (op == "|" || op == "&") + if (op == "|" || op == "&") { BitFlags result = this; result.opOpAssign!op(flags); @@ -10013,7 +10022,7 @@ public: } auto opBinary(string op)(E flag) const - if (op == "|" || op == "&") + if (op == "|" || op == "&") { BitFlags result = this; result.opOpAssign!op(flag); @@ -10021,7 +10030,7 @@ public: } auto opBinaryRight(string op)(E flag) const - if (op == "|" || op == "&") + if (op == "|" || op == "&") { return opBinary!op(flag); } @@ -10653,25 +10662,29 @@ struct Ternary $(TR $(TD `unknown`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `unknown`) $(TD `unknown`)) ) */ - Ternary opUnary(string s)() if (s == "~") + Ternary opUnary(string s)() + if (s == "~") { return make((386 >> value) & 6); } /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "|") + Ternary opBinary(string s)(Ternary rhs) + if (s == "|") { return make((25_512 >> (value + rhs.value)) & 6); } /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "&") + Ternary opBinary(string s)(Ternary rhs) + if (s == "&") { return make((26_144 >> (value + rhs.value)) & 6); } /// ditto - Ternary opBinary(string s)(Ternary rhs) if (s == "^") + Ternary opBinary(string s)(Ternary rhs) + if (s == "^") { return make((26_504 >> (value + rhs.value)) & 6); } @@ -10937,7 +10950,8 @@ struct RefCounted(T, RefCountedAutoInitialize autoInit = return _refCounted; } - this(A...)(auto ref A args) if (A.length > 0) + this(A...)(auto ref A args) + if (A.length > 0) out { assert(refCountedStore.isInitialized); diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d index f7610c0..34d15e0 100644 --- a/libphobos/src/std/uni/package.d +++ b/libphobos/src/std/uni/package.d @@ -16,6 +16,7 @@ $(TR $(TD Decode) $(TD $(LREF byGrapheme) $(LREF decodeGrapheme) $(LREF graphemeStride) + $(LREF popGrapheme) )) $(TR $(TD Comparison) $(TD $(LREF icmp) @@ -708,8 +709,8 @@ import std.meta : AliasSeq; import std.range.primitives : back, ElementEncodingType, ElementType, empty, front, hasLength, hasSlicing, isForwardRange, isInputRange, isRandomAccessRange, popFront, put, save; -import std.traits : isConvertibleToString, isIntegral, isSomeChar, - isSomeString, Unqual, isDynamicArray; +import std.traits : isAutodecodableString, isConvertibleToString, isIntegral, + isSomeChar, isSomeString, Unqual, isDynamicArray; // debug = std_uni; import std.internal.unicode_tables; // generated file @@ -961,7 +962,7 @@ struct MultiArray(Types...) } void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { import std.format.write : formattedWrite; formattedWrite(sink, "[%( 0x%x, %)]", offsets[]); @@ -1652,7 +1653,7 @@ if (is(T : ElementType!Range)) template sharMethod(alias uniLowerBound) { size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle) - if (is(T : ElementType!Range)) + if (is(T : ElementType!Range)) { import std.functional : binaryFun; import std.math.algebraic : nextPow2, truncPow2; @@ -1768,19 +1769,19 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) + if (!isInputRange!V) { arr ~= force!T(value); } static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V) + if (isInputRange!V) { insertInPlace(arr, arr.length, value); } static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && is(Unqual!T == T)) + if (isDynamicArray!T && is(Unqual!T == T)) { debug { @@ -1790,7 +1791,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && !is(Unqual!T == T)) + if (isDynamicArray!T && !is(Unqual!T == T)) { arr = null; } @@ -1845,7 +1846,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) + if (!isInputRange!V) { if (arr.length == size_t.max) assert(0); arr = realloc(arr, arr.length+1); @@ -1862,7 +1863,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V && hasLength!V) + if (isInputRange!V && hasLength!V) { import core.checkedint : addu; bool overflow; @@ -2058,7 +2059,7 @@ public struct InversionList(SP=GcPolicy) Construct from another code point set of any type. */ this(Set)(Set set) pure - if (isCodepointSet!Set) + if (isCodepointSet!Set) { uint[] arr; foreach (v; set.byInterval) @@ -2073,7 +2074,7 @@ public struct InversionList(SP=GcPolicy) Construct a set from a forward range of code point intervals. */ this(Range)(Range intervals) pure - if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) + if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) { uint[] arr; foreach (v; intervals) @@ -2245,7 +2246,7 @@ public: ) */ This opBinary(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { static if (op == "&" || op == "|" || op == "~") {// symmetric ops thus can swap arguments to reuse r-value @@ -2310,7 +2311,7 @@ public: /// The 'op=' versions of the above overloaded operators. ref This opOpAssign(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { static if (op == "|") // union { @@ -2342,7 +2343,7 @@ public: the same as $(LREF opIndex). */ bool opBinaryRight(string op: "in", U)(U ch) const - if (is(U : dchar)) + if (is(U : dchar)) { return this[ch]; } @@ -2522,7 +2523,7 @@ private: package(std) // used from: std.regex.internal.parser ref intersect(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker mark; foreach ( i; rhs.byInterval) @@ -2556,7 +2557,7 @@ private: // same as the above except that skip & drop parts are swapped package(std) // used from: std.regex.internal.parser ref sub(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker mark; foreach (i; rhs.byInterval) @@ -2569,7 +2570,7 @@ private: package(std) // used from: std.regex.internal.parse ref add(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker start; foreach (i; rhs.byInterval) @@ -3206,7 +3207,7 @@ struct CowArray(SP=GcPolicy) } this(Range)(Range range) - if (isInputRange!Range && hasLength!Range) + if (isInputRange!Range && hasLength!Range) { import std.algorithm.mutation : copy; length = range.length; @@ -3214,7 +3215,7 @@ struct CowArray(SP=GcPolicy) } this(Range)(Range range) - if (isForwardRange!Range && !hasLength!Range) + if (isForwardRange!Range && !hasLength!Range) { import std.algorithm.mutation : copy; import std.range.primitives : walkLength; @@ -3336,7 +3337,7 @@ struct CowArray(SP=GcPolicy) } void append(Range)(Range range) - if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) + if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) { size_t nl = length + range.length; length = nl; @@ -3793,7 +3794,7 @@ auto arrayRepr(T)(T x) template mapTrieIndex(Prefix...) { size_t mapTrieIndex(Key)(Key key) - if (isValidPrefixForTrie!(Key, Prefix)) + if (isValidPrefixForTrie!(Key, Prefix)) { alias p = Prefix; size_t idx; @@ -4184,7 +4185,7 @@ if (isValidPrefixForTrie!(Key, Args) /// void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { _table.store(sink); } @@ -4285,7 +4286,7 @@ public template codepointSetTrie(sizes...) if (sumOfIntegerTuple!sizes == 21) { auto codepointSetTrie(Set)(Set set) - if (isCodepointSet!Set) + if (isCodepointSet!Set) { auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false); foreach (ival; set.byInterval) @@ -4322,7 +4323,7 @@ if (sumOfIntegerTuple!sizes == 21) static if (is(TypeOfBitPacked!T == bool)) { auto codepointTrie(Set)(const scope Set set) - if (isCodepointSet!Set) + if (isCodepointSet!Set) { return codepointSetTrie(set); } @@ -4337,9 +4338,9 @@ if (sumOfIntegerTuple!sizes == 21) // unsorted range of pairs /// auto codepointTrie(R)(R range, T defValue=T.init) - if (isInputRange!R - && is(typeof(ElementType!R.init[0]) : T) - && is(typeof(ElementType!R.init[1]) : dchar)) + if (isInputRange!R + && is(typeof(ElementType!R.init[0]) : T) + && is(typeof(ElementType!R.init[1]) : dchar)) { // build from unsorted array of pairs // TODO: expose index sorting functions for Trie @@ -4467,8 +4468,8 @@ if (isValidArgsForTrie!(Key, Args)) $(REF setUnion, std,_algorithm). */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) + if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) + && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (v; range) @@ -4487,9 +4488,9 @@ if (isValidArgsForTrie!(Key, Args)) and `filler` is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) - && is(typeof(Range.init.front[1]) : Key)) + if (is(TypeOfBitPacked!Value == bool) + && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) + && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (ival; range) @@ -4498,9 +4499,9 @@ if (isValidArgsForTrie!(Key, Args)) } auto buildTrie(Range)(Range range, Value filler, bool unsorted) - if (isInputRange!Range - && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) + if (isInputRange!Range + && is(typeof(Range.init.front[0]) : Value) + && is(typeof(Range.init.front[1]) : Key)) { import std.algorithm.sorting : multiSort; alias Comps = GetComparators!(Prefix.length); @@ -4519,8 +4520,8 @@ if (isValidArgsForTrie!(Key, Args)) If no filler provided keys map to true, and `filler` is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front) : Key)) + if (is(TypeOfBitPacked!Value == bool) + && isInputRange!Range && is(typeof(Range.init.front) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (v; range) @@ -4533,7 +4534,7 @@ if (isValidArgsForTrie!(Key, Args)) of values where array index serves as key. */ auto buildTrie()(Value[] array, Value filler=Value.init) - if (isUnsigned!Key) + if (isUnsigned!Key) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (idx, v; array) @@ -4595,21 +4596,21 @@ public struct MatcherConcept of the result of test.) */ public bool match(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool skip(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool test(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } @@ -4765,7 +4766,7 @@ template Utf8Matcher() } static auto encode(size_t sz)(dchar ch) - if (sz > 1) + if (sz > 1) { import std.utf : encodeUTF = encode; char[4] buf; @@ -4821,8 +4822,8 @@ template Utf8Matcher() enum dispatch = genDispatch(); public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.skipOnMatch; assert(!inp.empty); @@ -4846,8 +4847,8 @@ template Utf8Matcher() static if (Sizes.length == 4) // can skip iff can detect all encodings { public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.alwaysSkip; assert(!inp.empty); @@ -4868,8 +4869,8 @@ template Utf8Matcher() } public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.neverSkip; assert(!inp.empty); @@ -4887,19 +4888,19 @@ template Utf8Matcher() } bool match(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -5056,8 +5057,8 @@ template Utf16Matcher() mixin template DefMatcher() { public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.skipOnMatch; assert(!inp.empty); @@ -5083,8 +5084,8 @@ template Utf16Matcher() static if (Sizes.length == 2) { public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.alwaysSkip; assert(!inp.empty); @@ -5105,8 +5106,8 @@ template Utf16Matcher() } public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.neverSkip; assert(!inp.empty); @@ -5118,19 +5119,19 @@ template Utf16Matcher() } bool match(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -5139,7 +5140,7 @@ template Utf16Matcher() } struct Impl(Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: import std.meta : allSatisfy; @@ -5229,7 +5230,7 @@ template Utf16Matcher() } struct CherryPick(I, Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: import std.meta : allSatisfy; @@ -6105,7 +6106,7 @@ template SetSearcher(alias table, string kind) { /// Run-time checked search. static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) + if (is(C : dchar)) { import std.conv : to; CodepointSet set; @@ -6765,7 +6766,7 @@ struct UnicodeSetParser(Range) sets. */ static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) + if (is(C : dchar)) { return loadAny(name); } @@ -7148,17 +7149,25 @@ private immutable TransformRes TransformRes.goOn ]; -template genericDecodeGrapheme(bool getValue) -{ - static if (getValue) +enum GraphemeRet { none, step, value } + +template genericDecodeGrapheme(GraphemeRet retType) +{ alias Ret = GraphemeRet; + + static if (retType == Ret.value) alias Value = Grapheme; - else + else static if (retType == Ret.step) + alias Value = size_t; + else static if (retType == Ret.none) alias Value = void; Value genericDecodeGrapheme(Input)(ref Input range) { - static if (getValue) - Grapheme grapheme; + static if (retType == Ret.value) + Grapheme result; + else static if (retType == Ret.step) + size_t result = 0; + auto state = GraphemeState.Start; dchar ch; @@ -7173,8 +7182,10 @@ template genericDecodeGrapheme(bool getValue) with(TransformRes) { case goOn: - static if (getValue) - grapheme ~= ch; + static if (retType == Ret.value) + result ~= ch; + else static if (retType == Ret.step) + result++; range.popFront(); continue; @@ -7182,8 +7193,10 @@ template genericDecodeGrapheme(bool getValue) goto rerun; case retInclude: - static if (getValue) - grapheme ~= ch; + static if (retType == Ret.value) + result ~= ch; + else static if (retType == Ret.step) + result++; range.popFront(); break outer; @@ -7192,8 +7205,8 @@ template genericDecodeGrapheme(bool getValue) } } - static if (getValue) - return grapheme; + static if (retType != Ret.none) + return result; } } @@ -7217,7 +7230,7 @@ if (is(C : dchar)) { auto src = input[index..$]; auto n = src.length; - genericDecodeGrapheme!(false)(src); + genericDecodeGrapheme!(GraphemeRet.none)(src); return n - src.length; } @@ -7279,7 +7292,7 @@ if (is(C : dchar)) Grapheme decodeGrapheme(Input)(ref Input inp) if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) { - return genericDecodeGrapheme!true(inp); + return genericDecodeGrapheme!(GraphemeRet.value)(inp); } @safe unittest @@ -7305,6 +7318,73 @@ if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) } /++ + Reads one full grapheme cluster from an + $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of dchar `inp`, + but doesn't return it. Instead returns the number of code units read. + This differs from number of code points read only if `input` is an + autodecodable string. + + Note: + This function modifies `inp` and thus `inp` + must be an L-value. ++/ +size_t popGrapheme(Input)(ref Input inp) +if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) +{ + static if (isAutodecodableString!Input || hasLength!Input) + { + // Why count each step in the decoder when you can just + // measure the grapheme in one go? + auto n = inp.length; + genericDecodeGrapheme!(GraphemeRet.none)(inp); + return n - inp.length; + } + else return genericDecodeGrapheme!(GraphemeRet.step)(inp); +} + +/// +@safe pure unittest +{ + // Two Union Jacks of the Great Britain in each + string s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + wstring ws = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + dstring ds = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + + // String pop length in code units, not points. + assert(s.popGrapheme() == 8); + assert(ws.popGrapheme() == 4); + assert(ds.popGrapheme() == 2); + + assert(s == "\U0001F1EC\U0001F1E7"); + assert(ws == "\U0001F1EC\U0001F1E7"); + assert(ds == "\U0001F1EC\U0001F1E7"); + + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; + + // Also works for non-random access ranges as long as the + // character type is 32-bit. + auto testPiece = "\r\nhello!"d.filter!(x => !x.isAlpha); + // Windows-style line ending is two code points in a single grapheme. + assert(testPiece.popGrapheme() == 2); + assert(testPiece.equal("!"d)); +} + +// Attribute compliance test. Should be nothrow `@nogc` when +// no autodecoding needed. +@safe pure nothrow @nogc unittest +{ + import std.algorithm.iteration : filter; + + auto str = "abcdef"d; + assert(str.popGrapheme() == 1); + + // also test with non-random access + auto filtered = "abcdef"d.filter!(x => x%2); + assert(filtered.popGrapheme() == 1); +} + +/++ $(P Iterate a string by $(LREF Grapheme).) $(P Useful for doing string manipulation that needs to be aware @@ -7556,15 +7636,15 @@ if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)) public: /// Ctor this(C)(const scope C[] chars...) - if (is(C : dchar)) + if (is(C : dchar)) { this ~= chars; } ///ditto this(Input)(Input seq) - if (!isDynamicArray!Input - && isInputRange!Input && is(ElementType!Input : dchar)) + if (!isDynamicArray!Input + && isInputRange!Input && is(ElementType!Input : dchar)) { this ~= seq; } @@ -7683,7 +7763,7 @@ public: /// Append all $(CHARACTERS) from the input range `inp` to this Grapheme. ref opOpAssign(string op, Input)(scope Input inp) - if (isInputRange!Input && is(ElementType!Input : dchar)) + if (isInputRange!Input && is(ElementType!Input : dchar)) { static if (op == "~") { @@ -7722,7 +7802,7 @@ public: @property bool valid()() /*const*/ { auto r = this[]; - genericDecodeGrapheme!false(r); + genericDecodeGrapheme!(GraphemeRet.none)(r); return r.length == 0; } @@ -9868,7 +9948,7 @@ private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn) { void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx, size_t destIdx) @trusted pure - if (is(C == char) || is(C == wchar) || is(C == dchar)) + if (is(C == char) || is(C == wchar) || is(C == dchar)) { import std.utf : decode; alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn); diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d index 9a326a5..7db120b 100644 --- a/libphobos/src/std/utf.d +++ b/libphobos/src/std/utf.d @@ -4303,13 +4303,13 @@ if (isSomeChar!C) else: auto ref byUTF(R)(R r) - if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) + if (isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) { return byUTF(r.byCodeUnit()); } auto ref byUTF(R)(R r) - if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) + if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) { static if (is(immutable ElementEncodingType!R == immutable RC, RC) && is(RC == C)) { diff --git a/libphobos/src/std/uuid.d b/libphobos/src/std/uuid.d index b8cac6e..09ce2f7 100644 --- a/libphobos/src/std/uuid.d +++ b/libphobos/src/std/uuid.d @@ -275,7 +275,7 @@ public struct UUID * You need to pass exactly 16 ubytes. */ @safe pure this(T...)(T uuidData) - if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) + if (uuidData.length == 16 && allSatisfy!(isIntegral, T)) { import std.conv : to; @@ -331,7 +331,8 @@ public struct UUID * * For a less strict parser, see $(LREF parseUUID) */ - this(T)(in T[] uuid) if (isSomeChar!T) + this(T)(in T[] uuid) + if (isSomeChar!T) { import std.conv : to, parse; if (uuid.length < 36) diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d index f7832104..a0b5644 100644 --- a/libphobos/src/std/variant.d +++ b/libphobos/src/std/variant.d @@ -658,7 +658,7 @@ public: /// Allows assignment from a subset algebraic type this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T value) - if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) + if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) { opAssign(value); } @@ -735,7 +735,7 @@ public: // Allow assignment from another variant which is a subset of this one VariantN opAssign(T : VariantN!(tsize, Types), size_t tsize, Types...)(T rhs) - if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) + if (!is(T : VariantN) && Types.length > 0 && allSatisfy!(allowed, Types)) { // discover which type rhs is actually storing foreach (V; T.AllowedTypes) @@ -1098,7 +1098,7 @@ public: { return opLogic!(T, op)(lhs); } ///ditto VariantN opBinary(string op, T)(T rhs) - if (op == "~") + if (op == "~") { auto temp = this; temp ~= rhs; @@ -1191,7 +1191,8 @@ public: If the `VariantN` contains an array, applies `dg` to each element of the array in turn. Otherwise, throws an exception. */ - int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) + int opApply(Delegate)(scope Delegate dg) + if (is(Delegate == delegate)) { alias A = Parameters!(Delegate)[0]; if (type == typeid(A[])) @@ -2410,7 +2411,7 @@ if (Handlers.length > 0) { /// auto visit(VariantType)(VariantType variant) - if (isAlgebraic!VariantType) + if (isAlgebraic!VariantType) { return visitImpl!(true, VariantType, Handlers)(variant); } @@ -2553,7 +2554,7 @@ if (Handlers.length > 0) { /// auto tryVisit(VariantType)(VariantType variant) - if (isAlgebraic!VariantType) + if (isAlgebraic!VariantType) { return visitImpl!(false, VariantType, Handlers)(variant); } |