diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2023-07-09 22:08:36 +0200 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2023-07-09 22:08:36 +0200 |
commit | 3b007164b3ef114c3c86c42ca2455f8f2696fb0d (patch) | |
tree | 340037c67d4a2a57774103d54fa103390611f6e5 /libphobos/src/std | |
parent | d6c1d7c4009bfe759719675ce3bc03ca503b9bf4 (diff) | |
download | gcc-3b007164b3ef114c3c86c42ca2455f8f2696fb0d.zip gcc-3b007164b3ef114c3c86c42ca2455f8f2696fb0d.tar.gz gcc-3b007164b3ef114c3c86c42ca2455f8f2696fb0d.tar.bz2 |
d: Merge upstream dmd, druntime 28a3b24c2e, phobos 8ab95ded5.
D front-end changes:
- Import dmd v2.104.0-beta.1.
- Better error message when attribute inference fails down the
call stack.
- Using `;' as an empty statement has been turned into an error.
- Using `in' parameters with non- `extern(D)' or `extern(C++)'
functions is deprecated.
- `in ref' on parameters has been deprecated in favor of
`-preview=in'.
- Throwing `immutable', `const', `inout', and `shared' qualified
objects is now deprecated.
- User Defined Attributes now parse Template Arguments.
D runtime changes:
- Import druntime v2.104.0-beta.1.
Phobos changes:
- Import phobos v2.104.0-beta.1.
- Better static assert messages when instantiating
`std.algorithm.comparison.clamp' with wrong inputs.
- `std.typecons.Rebindable' now supports all types.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd 28a3b24c2e.
* dmd/VERSION: Bump version to v2.104.0-beta.1.
* d-codegen.cc (build_bounds_slice_condition): Update for new
front-end interface.
* d-lang.cc (d_init_options): Likewise.
(d_handle_option): Likewise.
(d_post_options): Initialize global.compileEnv.
* expr.cc (ExprVisitor::visit (CatExp *)): Replace code generation
with new front-end lowering.
(ExprVisitor::visit (LoweredAssignExp *)): New method.
(ExprVisitor::visit (StructLiteralExp *)): Don't generate static
initializer symbols for structs defined in C sources.
* runtime.def (ARRAYCATT): Remove.
(ARRAYCATNTX): Remove.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime 28a3b24c2e.
* src/MERGE: Merge upstream phobos 8ab95ded5.
gcc/testsuite/ChangeLog:
* gdc.dg/rtti1.d: Move array concat testcase to ...
* gdc.dg/nogc1.d: ... here. New test.
Diffstat (limited to 'libphobos/src/std')
-rw-r--r-- | libphobos/src/std/algorithm/comparison.d | 23 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/iteration.d | 28 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/searching.d | 68 | ||||
-rw-r--r-- | libphobos/src/std/array.d | 187 | ||||
-rw-r--r-- | libphobos/src/std/concurrency.d | 4 | ||||
-rw-r--r-- | libphobos/src/std/container/dlist.d | 36 | ||||
-rw-r--r-- | libphobos/src/std/experimental/allocator/building_blocks/region.d | 20 | ||||
-rw-r--r-- | libphobos/src/std/file.d | 4 | ||||
-rw-r--r-- | libphobos/src/std/json.d | 44 | ||||
-rw-r--r-- | libphobos/src/std/net/curl.d | 35 | ||||
-rw-r--r-- | libphobos/src/std/path.d | 3 | ||||
-rw-r--r-- | libphobos/src/std/random.d | 10 | ||||
-rw-r--r-- | libphobos/src/std/range/package.d | 619 | ||||
-rw-r--r-- | libphobos/src/std/range/primitives.d | 3 | ||||
-rw-r--r-- | libphobos/src/std/regex/internal/backtracking.d | 7 | ||||
-rw-r--r-- | libphobos/src/std/regex/internal/thompson.d | 7 | ||||
-rw-r--r-- | libphobos/src/std/stdio.d | 50 | ||||
-rw-r--r-- | libphobos/src/std/traits.d | 71 | ||||
-rw-r--r-- | libphobos/src/std/typecons.d | 340 |
19 files changed, 1196 insertions, 363 deletions
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d index 5ecb4f6..5c70960 100644 --- a/libphobos/src/std/algorithm/comparison.d +++ b/libphobos/src/std/algorithm/comparison.d @@ -577,19 +577,20 @@ Returns: and `T3` are different. */ T1 clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) -if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val)) - && (is(T2 : T1) && is(T3 : T1))) -// cannot use : -// `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1)) -// because of https://issues.dlang.org/show_bug.cgi?id=16235. -// Once that is fixed, we can simply use the ternary in both the template constraint -// and the template body -in { + static assert(is(T2 : T1), "T2 of type '", T2.stringof + , "' must be implicitly convertible to type of T1 '" + , T1.stringof, "'"); + static assert(is(T3 : T1), "T3 of type '", T3.stringof + , "' must be implicitly convertible to type of T1 '" + , T1.stringof, "'"); + assert(!lower.greaterThan(upper), "Lower can't be greater than upper."); -} -do -{ + + // `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1)) + // because of https://issues.dlang.org/show_bug.cgi?id=16235. + // Once that is fixed, we can simply use the ternary in both the template constraint + // and the template body if (val.lessThan(lower)) return lower; else if (val.greaterThan(upper)) diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index 8236076..9f5a6ac 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -3632,18 +3632,18 @@ auto joiner(RoR, Separator)(RoR r, Separator sep) /// Ditto auto joiner(RoR)(RoR r) -if (isInputRange!RoR && isInputRange!(ElementType!RoR)) +if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR))) { static struct Result { private: RoR _items; - ElementType!RoR _current; + Unqual!(ElementType!RoR) _current; enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) && isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR); static if (isBidirectional) { - ElementType!RoR _currentBack; + Unqual!(ElementType!RoR) _currentBack; bool reachedFinalElement; } @@ -4293,6 +4293,28 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) assert([only(S(null))].joiner.front == S(null)); } +// https://issues.dlang.org/show_bug.cgi?id=22785 +@safe unittest +{ + + import std.algorithm.iteration : joiner, map; + import std.array : array; + + static immutable struct S + { + int value; + } + + static immutable struct T + { + S[] arr; + } + + auto range = [T([S(3)]), T([S(4), S(5)])]; + + assert(range.map!"a.arr".joiner.array == [S(3), S(4), S(5)]); +} + /++ Implements the homonym function (also known as `accumulate`, $(D compress), `inject`, or `foldl`) present in various programming diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index 15f7ca9..ee318c8 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -119,12 +119,9 @@ template all(alias pred = "a") Performs (at most) $(BIGOH range.length) evaluations of `pred`. +/ bool all(Range)(Range range) - if (isInputRange!Range) + if (isInputRange!Range && + (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front))))) { - static assert(is(typeof(unaryFun!pred(range.front))), - "`" ~ (isSomeString!(typeof(pred)) - ? pred.stringof[1..$-1] : pred.stringof) - ~ "` isn't a unary predicate function for range.front"); import std.functional : not; return find!(not!(unaryFun!pred))(range).empty; @@ -172,7 +169,8 @@ template any(alias pred = "a") Performs (at most) $(BIGOH range.length) evaluations of `pred`. +/ bool any(Range)(Range range) - if (isInputRange!Range && is(typeof(unaryFun!pred(range.front)))) + if (isInputRange!Range && + (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front))))) { return !find!pred(range).empty; } @@ -1294,17 +1292,6 @@ if (isInputRange!R && private enum bool hasConstEmptyMember(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool); -// Rebindable doesn't work with structs -// see: https://github.com/dlang/phobos/pull/6136 -private template RebindableOrUnqual(T) -{ - import std.typecons : Rebindable; - static if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T) - alias RebindableOrUnqual = Rebindable!T; - else - alias RebindableOrUnqual = Unqual!T; -} - /** Iterates the passed range and selects the extreme element with `less`. If the extreme element occurs multiple time, the first occurrence will be @@ -1313,8 +1300,8 @@ returned. Params: map = custom accessor for the comparison key selector = custom mapping for the extrema selection - seed = custom seed to use as initial element r = Range from which the extreme value will be selected + seedElement = custom seed to use as initial element Returns: The extreme value according to `map` and `selector` of the passed-in values. @@ -1328,10 +1315,19 @@ in } do { + import std.typecons : Rebindable; + alias Element = ElementType!Range; - RebindableOrUnqual!Element seed = r.front; + Rebindable!Element seed = r.front; r.popFront(); - return extremum!(map, selector)(r, seed); + static if (is(Rebindable!Element == T[], T)) + { + return extremum!(map, selector)(r, seed); + } + else + { + return extremum!(map, selector)(r, seed.get); + } } private auto extremum(alias map, alias selector = "a < b", Range, @@ -1341,13 +1337,14 @@ if (isInputRange!Range && !isInfinite!Range && !is(CommonType!(ElementType!Range, RangeElementType) == void) && is(typeof(unaryFun!map(ElementType!(Range).init)))) { + import std.typecons : Rebindable; + alias mapFun = unaryFun!map; alias selectorFun = binaryFun!selector; alias Element = ElementType!Range; alias CommonElement = CommonType!(Element, RangeElementType); - RebindableOrUnqual!CommonElement extremeElement = seedElement; - + Rebindable!CommonElement extremeElement = seedElement; // if we only have one statement in the loop, it can be optimized a lot better static if (__traits(isSame, map, a => a)) @@ -1408,7 +1405,15 @@ if (isInputRange!Range && !isInfinite!Range && } } } - return extremeElement; + // Rebindable is an alias to T for arrays + static if (is(typeof(extremeElement) == T[], T)) + { + return extremeElement; + } + else + { + return extremeElement.get; + } } private auto extremum(alias selector = "a < b", Range)(Range r) @@ -1493,6 +1498,10 @@ if (isInputRange!Range && !isInfinite!Range && assert(d.extremum!`a > b` == 10); assert(d.extremum!(a => a, `a > b`) == 10); } + + // compiletime + enum ctExtremum = iota(1, 5).extremum; + assert(ctExtremum == 1); } @nogc @safe nothrow pure unittest @@ -1524,6 +1533,17 @@ if (isInputRange!Range && !isInfinite!Range && assert(arr.extremum!"a.val".val == 0); } +// https://issues.dlang.org/show_bug.cgi?id=22786 +@nogc @safe nothrow pure unittest +{ + struct S + { + immutable int value; + } + + assert([S(5), S(6)].extremum!"a.value" == S(5)); +} + // find /** Finds an individual element in an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). @@ -1552,7 +1572,7 @@ Complexity: `find` performs $(BIGOH walkLength(haystack)) evaluations of `pred`. There are specializations that improve performance by taking advantage of $(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) - or $(REF_ALTTEXT random access, isRandomAccess, std,range,primitives) + or $(REF_ALTTEXT random access, isRandomAccessRange, std,range,primitives) ranges (where possible). Params: diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index 4584dcc..2652803 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -2565,33 +2565,8 @@ E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to) if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || is(Unqual!E : Unqual!R1)) { - import std.algorithm.searching : find; - import std.range : dropOne; - - static if (isInputRange!R1) - { - if (from.empty) return subject; - alias rSave = a => a.save; - } - else - { - alias rSave = a => a; - } - - auto balance = find(subject, rSave(from)); - if (balance.empty) - return subject; - - auto app = appender!(E[])(); - app.put(subject[0 .. subject.length - balance.length]); - app.put(rSave(to)); - // replacing an element in an array is different to a range replacement - static if (is(Unqual!E : Unqual!R1)) - replaceInto(app, balance.dropOne, from, to); - else - replaceInto(app, balance[from.length .. $], from, to); - - return app.data; + size_t changed = 0; + return replace(subject, from, to, changed); } /// @@ -2645,55 +2620,87 @@ if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2) } /++ - Replace occurrences of `from` with `to` in `subject` and output the result into - `sink`. + Replace occurrences of `from` with `to` in `subject` in a new array. + `changed` counts how many replacements took place. Params: - sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) subject = the array to scan from = the item to replace to = the item to replace all instances of `from` with + changed = the number of replacements - See_Also: - $(REF substitute, std,algorithm,iteration) for a lazy replace. + Returns: + A new array without changing the contents of `subject`, or the original + array if no match is found. +/ -void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to) -if (isOutputRange!(Sink, E) && - ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || - is(Unqual!E : Unqual!R1))) +E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to, ref size_t changed) +if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || + is(Unqual!E : Unqual!R1)) { import std.algorithm.searching : find; import std.range : dropOne; static if (isInputRange!R1) { - if (from.empty) - { - sink.put(subject); - return; - } + if (from.empty) return subject; alias rSave = a => a.save; } else { alias rSave = a => a; } - for (;;) - { - auto balance = find(subject, rSave(from)); - if (balance.empty) - { - sink.put(subject); - break; - } - sink.put(subject[0 .. subject.length - balance.length]); - sink.put(rSave(to)); - // replacing an element in an array is different to a range replacement - static if (is(Unqual!E : Unqual!R1)) - subject = balance.dropOne; - else - subject = balance[from.length .. $]; - } + + auto balance = find(subject, rSave(from)); + if (balance.empty) + return subject; + + auto app = appender!(E[])(); + app.put(subject[0 .. subject.length - balance.length]); + app.put(rSave(to)); + ++changed; + // replacing an element in an array is different to a range replacement + static if (is(Unqual!E : Unqual!R1)) + replaceInto(app, balance.dropOne, from, to, changed); + else + replaceInto(app, balance[from.length .. $], from, to, changed); + + return app.data; +} + +/// +@safe unittest +{ + size_t changed = 0; + assert("Hello Wörld".replace("o Wö", "o Wo", changed) == "Hello World"); + assert(changed == 1); + + changed = 0; + assert("Hello Wörld".replace("l", "h", changed) == "Hehho Wörhd"); + import std.stdio : writeln; + writeln(changed); + assert(changed == 3); +} + +/++ + Replace occurrences of `from` with `to` in `subject` and output the result into + `sink`. + + Params: + sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) + subject = the array to scan + from = the item to replace + to = the item to replace all instances of `from` with + + See_Also: + $(REF substitute, std,algorithm,iteration) for a lazy replace. + +/ +void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to) +if (isOutputRange!(Sink, E) && + ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || + is(Unqual!E : Unqual!R1))) +{ + size_t changed = 0; + replaceInto(sink, subject, from, to, changed); } /// @@ -2784,6 +2791,72 @@ if (isOutputRange!(Sink, E) && } /++ + Replace occurrences of `from` with `to` in `subject` and output the result into + `sink`. `changed` counts how many replacements took place. + + Params: + sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) + subject = the array to scan + from = the item to replace + to = the item to replace all instances of `from` with + changed = the number of replacements + +/ +void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to, ref size_t changed) +if (isOutputRange!(Sink, E) && + ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) || + is(Unqual!E : Unqual!R1))) +{ + import std.algorithm.searching : find; + import std.range : dropOne; + + static if (isInputRange!R1) + { + if (from.empty) + { + sink.put(subject); + return; + } + alias rSave = a => a.save; + } + else + { + alias rSave = a => a; + } + for (;;) + { + auto balance = find(subject, rSave(from)); + if (balance.empty) + { + sink.put(subject); + break; + } + sink.put(subject[0 .. subject.length - balance.length]); + sink.put(rSave(to)); + ++changed; + // replacing an element in an array is different to a range replacement + static if (is(Unqual!E : Unqual!R1)) + subject = balance.dropOne; + else + subject = balance[from.length .. $]; + } +} + +/// +@safe unittest +{ + auto arr = [1, 2, 3, 4, 5]; + auto from = [2, 3]; + auto to = [4, 6]; + auto sink = appender!(int[])(); + + size_t changed = 0; + replaceInto(sink, arr, from, to, changed); + + assert(sink.data == [1, 4, 6, 4, 5]); + assert(changed == 1); +} + +/++ Replaces elements from `array` with indices ranging from `from` (inclusive) to `to` (exclusive) with the range `stuff`. diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d index 7fe52ba..267f682 100644 --- a/libphobos/src/std/concurrency.d +++ b/libphobos/src/std/concurrency.d @@ -2270,7 +2270,9 @@ private if (range.front.convertsTo!(Throwable)) throw range.front.get!(Throwable); else if (range.front.convertsTo!(shared(Throwable))) - throw range.front.get!(shared(Throwable)); + /* Note: a shared type can be caught without the shared qualifier + * so throwing shared will be an error */ + throw cast() range.front.get!(shared(Throwable)); else throw new PriorityMessageException(range.front.data); } diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d index 11e3883..4fdf13d 100644 --- a/libphobos/src/std/container/dlist.d +++ b/libphobos/src/std/container/dlist.d @@ -198,8 +198,10 @@ struct DList(T) this (BaseNode _base, T _payload) { + import std.algorithm.mutation : move; + this._base = _base; - this._payload = _payload; + this._payload = move(_payload); } inout(BaseNode)* asBaseNode() inout @trusted @@ -216,7 +218,9 @@ struct DList(T) //Construct as new PayNode, and returns it as a BaseNode. static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null) { - return (new PayNode(BaseNode(prev, next), arg)).asBaseNode(); + import std.algorithm.mutation : move; + + return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode(); } void initialize() nothrow @safe pure @@ -721,7 +725,9 @@ Complexity: $(BIGOH n) */ bool linearRemoveElement(T value) { - auto n1 = findNodeByValue(_root, value); + import std.algorithm.mutation : move; + + auto n1 = findNodeByValue(_root, move(value)); if (n1) { auto n2 = n1._next._next; @@ -1118,3 +1124,27 @@ private: a.insertFront(iota(0, 5)); // can insert range with non-ref front assert(a.front == 0 && a.back == 4); } + +// https://issues.dlang.org/show_bug.cgi?id=22147 +@safe unittest +{ + import std.algorithm.mutation : move; + + static struct Item + { + @disable this(this); + + int x; + } + + auto list = DList!Item(); + list.insertFront(Item(1)); + assert(list[].walkLength == 1); + assert(list.front.x == 1); + auto item = list.moveFront; + item.x = 2; + list.front = move(item); + assert(list.front.x == 2); + list.removeFront(); + assert(list[].walkLength == 0); +} diff --git a/libphobos/src/std/experimental/allocator/building_blocks/region.d b/libphobos/src/std/experimental/allocator/building_blocks/region.d index 8c39784..a23746a 100644 --- a/libphobos/src/std/experimental/allocator/building_blocks/region.d +++ b/libphobos/src/std/experimental/allocator/building_blocks/region.d @@ -695,25 +695,15 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) import std.conv : to; import std.traits : hasMember; import std.typecons : Ternary; + import core.thread.types : isStackGrowingDown; static assert(minAlign.isGoodStaticAlignment); static assert(size >= minAlign); - version (X86) enum growDownwards = Yes.growDownwards; - else version (X86_64) enum growDownwards = Yes.growDownwards; - else version (ARM) enum growDownwards = Yes.growDownwards; - else version (AArch64) enum growDownwards = Yes.growDownwards; - else version (HPPA) enum growDownwards = No.growDownwards; - else version (PPC) enum growDownwards = Yes.growDownwards; - else version (PPC64) enum growDownwards = Yes.growDownwards; - else version (RISCV32) enum growDownwards = Yes.growDownwards; - else version (RISCV64) enum growDownwards = Yes.growDownwards; - else version (MIPS32) enum growDownwards = Yes.growDownwards; - else version (MIPS64) enum growDownwards = Yes.growDownwards; - else version (SPARC) enum growDownwards = Yes.growDownwards; - else version (SPARC64) enum growDownwards = Yes.growDownwards; - else version (SystemZ) enum growDownwards = Yes.growDownwards; - else static assert(0, "Dunno how the stack grows on this architecture."); + static if (isStackGrowingDown) + enum growDownwards = Yes.growDownwards; + else + enum growDownwards = No.growDownwards; @disable this(this); diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index b7bd3fc..5b8925d 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -310,6 +310,8 @@ Params: Returns: Untyped array of bytes _read. Throws: $(LREF FileException) on error. + +See_Also: $(REF readText, std,file) for reading and validating a text file. */ void[] read(R)(R name, size_t upTo = size_t.max) @@ -497,6 +499,8 @@ version (linux) @safe unittest Throws: $(LREF FileException) if there is an error reading the file, $(REF UTFException, std, utf) on UTF decoding error. + + See_Also: $(REF read, std,file) for reading a binary file. +/ S readText(S = string, R)(auto ref R name) if (isSomeString!S && (isSomeFiniteCharInputRange!R || is(StringTypeOf!R))) diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index 088e77f..219af71 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -1,7 +1,10 @@ // Written in the D programming language. /** -JavaScript Object Notation +Implements functionality to read and write JavaScript Object Notation values. + +JavaScript Object Notation is a lightweight data interchange format commonly used in web services and configuration files. +It's easy for humans to read and write, and it's easy for machines to parse and generate. Copyright: Copyright Jeremie Pelletier 2008 - 2009. License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). @@ -602,6 +605,45 @@ struct JSONValue assert(j.type == JSONType.object); } + /** + * An enum value that can be used to obtain a `JSONValue` representing + * an empty JSON object. + */ + enum emptyObject = JSONValue(string[string].init); + /// + @system unittest + { + JSONValue obj1 = JSONValue.emptyObject; + assert(obj1.type == JSONType.object); + obj1.object["a"] = JSONValue(1); + assert(obj1.object["a"] == JSONValue(1)); + + JSONValue obj2 = JSONValue.emptyObject; + assert("a" !in obj2.object); + obj2.object["b"] = JSONValue(5); + assert(obj1 != obj2); + } + + /** + * An enum value that can be used to obtain a `JSONValue` representing + * an empty JSON array. + */ + enum emptyArray = JSONValue(JSONValue[].init); + /// + @system unittest + { + JSONValue arr1 = JSONValue.emptyArray; + assert(arr1.type == JSONType.array); + assert(arr1.array.length == 0); + arr1.array ~= JSONValue("Hello"); + assert(arr1.array.length == 1); + assert(arr1.array[0] == JSONValue("Hello")); + + JSONValue arr2 = JSONValue.emptyArray; + assert(arr2.array.length == 0); + assert(arr1 != arr2); + } + void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue)) { assign(arg); diff --git a/libphobos/src/std/net/curl.d b/libphobos/src/std/net/curl.d index 42a34b9..2fcbf94 100644 --- a/libphobos/src/std/net/curl.d +++ b/libphobos/src/std/net/curl.d @@ -23,7 +23,7 @@ SMTP) ) ) Note: -You may need to link to the $(B curl) library, e.g. by adding $(D "libs": ["curl"]) +You may need to link with the $(B curl) library, e.g. by adding $(D "libs": ["curl"]) to your $(B dub.json) file if you are using $(LINK2 http://code.dlang.org, DUB). Windows x86 note: @@ -32,20 +32,19 @@ $(LINK2 https://downloads.dlang.org/other/index.html, download archive page). This module is not available for iOS, tvOS or watchOS. -Compared to using libcurl directly this module allows simpler client code for +Compared to using libcurl directly, this module allows simpler client code for common uses, requires no unsafe operations, and integrates better with the rest -of the language. Futhermore it provides $(MREF_ALTTEXT range, std,range) +of the language. Furthermore it provides $(MREF_ALTTEXT range, std,range) access to protocols supported by libcurl both synchronously and asynchronously. A high level and a low level API are available. The high level API is built entirely on top of the low level one. The high level API is for commonly used functionality such as HTTP/FTP get. The -$(LREF byLineAsync) and $(LREF byChunkAsync) provides asynchronous -$(MREF_ALTTEXT range, std,range) that performs the request in another -thread while handling a line/chunk in the current thread. +$(LREF byLineAsync) and $(LREF byChunkAsync) functions asynchronously +perform the request given, outputting the fetched content into a $(MREF_ALTTEXT range, std,range). -The low level API allows for streaming and other advanced features. +The low level API allows for streaming, setting request headers and cookies, and other advanced features. $(BOOKTABLE Cheat Sheet, $(TR $(TH Function Name) $(TH Description) @@ -79,18 +78,18 @@ byChunk("dlang.org", 10)) returns a range of ubyte[10] containing the dlang.org web page.) ) $(TR $(TDNW $(LREF byLineAsync)) $(TD $(D -byLineAsync("dlang.org")) returns a range of char[] containing the dlang.org web - page asynchronously.) +byLineAsync("dlang.org")) asynchronously returns a range of char[] containing the dlang.org web + page.) ) $(TR $(TDNW $(LREF byChunkAsync)) $(TD $(D -byChunkAsync("dlang.org", 10)) returns a range of ubyte[10] containing the -dlang.org web page asynchronously.) +byChunkAsync("dlang.org", 10)) asynchronously returns a range of ubyte[10] containing the +dlang.org web page.) ) $(LEADINGROW Low level ) -$(TR $(TDNW $(LREF HTTP)) $(TD `HTTP` struct for advanced usage)) -$(TR $(TDNW $(LREF FTP)) $(TD `FTP` struct for advanced usage)) -$(TR $(TDNW $(LREF SMTP)) $(TD `SMTP` struct for advanced usage)) +$(TR $(TDNW $(LREF HTTP)) $(TD Struct for advanced HTTP usage)) +$(TR $(TDNW $(LREF FTP)) $(TD Struct for advanced FTP usage)) +$(TR $(TDNW $(LREF SMTP)) $(TD Struct for advanced SMTP usage)) ) @@ -135,10 +134,10 @@ http.perform(); First, an instance of the reference-counted HTTP struct is created. Then the custom delegates are set. These will be called whenever the HTTP instance receives a header and a data buffer, respectively. In this simple example, the -headers are written to stdout and the data is ignored. If the request should be +headers are written to stdout and the data is ignored. If the request is stopped before it has finished then return something less than data.length from the onReceive callback. See $(LREF onReceiveHeader)/$(LREF onReceive) for more -information. Finally the HTTP request is effected by calling perform(), which is +information. Finally, the HTTP request is performed by calling perform(), which is synchronous. Source: $(PHOBOSSRC std/net/curl.d) @@ -147,8 +146,8 @@ Copyright: Copyright Jonas Drewsen 2011-2012 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonas Drewsen. Some of the SMTP code contributed by Jimmy Cao. -Credits: The functionally is based on $(HTTP curl.haxx.se/libcurl, libcurl). - LibCurl is licensed under an MIT/X derivative license. +Credits: The functionality is based on $(HTTP curl.haxx.se/libcurl, libcurl). + libcurl is licensed under an MIT/X derivative license. */ /* Copyright Jonas Drewsen 2011 - 2012. diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d index 4b5a7ef..63d60d1 100644 --- a/libphobos/src/std/path.d +++ b/libphobos/src/std/path.d @@ -1758,7 +1758,6 @@ immutable(C)[] buildNormalizedPath(C)(const(C[])[] paths...) if (isSomeChar!C) { import std.array : array; - import std.exception : assumeUnique; const(C)[] chained; foreach (path; paths) @@ -1770,7 +1769,7 @@ if (isSomeChar!C) } auto result = asNormalizedPath(chained); // .array returns a copy, so it is unique - return () @trusted { return assumeUnique(result.array); } (); + return result.array; } /// diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index 93be764..066ed17 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -812,7 +812,7 @@ Parameters for the generator. // Bitmasks used in the 'twist' part of the algorithm private enum UIntType lowerMask = (cast(UIntType) 1u << r) - 1, - upperMask = (~lowerMask) & this.max; + upperMask = (~lowerMask) & max; /* Collection of all state variables @@ -905,17 +905,17 @@ Parameters for the generator. private static void seedImpl(UIntType value, ref State mtState) @nogc { mtState.data[$ - 1] = value; - static if (this.max != UIntType.max) + static if (max != UIntType.max) { - mtState.data[$ - 1] &= this.max; + mtState.data[$ - 1] &= max; } foreach_reverse (size_t i, ref e; mtState.data[0 .. $ - 1]) { e = f * (mtState.data[i + 1] ^ (mtState.data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1)); - static if (this.max != UIntType.max) + static if (max != UIntType.max) { - e &= this.max; + e &= max; } } diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d index 888ac1a..7a724b0 100644 --- a/libphobos/src/std/range/package.d +++ b/libphobos/src/std/range/package.d @@ -235,7 +235,7 @@ public import std.range.primitives; public import std.typecons : Flag, Yes, No; import std.internal.attributes : betterC; -import std.meta : allSatisfy, anySatisfy, staticMap; +import std.meta : aliasSeqOf, allSatisfy, anySatisfy, staticMap; import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral, isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf; @@ -313,12 +313,16 @@ if (isBidirectionalRange!(Unqual!Range)) { @property void front(ElementType!R val) { - source.back = val; + import std.algorithm.mutation : move; + + source.back = move(val); } @property void back(ElementType!R val) { - source.front = val; + import std.algorithm.mutation : move; + + source.front = move(val); } } @@ -330,7 +334,9 @@ if (isBidirectionalRange!(Unqual!Range)) { void opIndexAssign(ElementType!R val, size_t n) { - source[retroIndex(n)] = val; + import std.algorithm.mutation : move; + + source[retroIndex(n)] = move(val); } } @@ -474,6 +480,19 @@ pure @safe nothrow @nogc unittest foreach (x; data.retro) {} } +pure @safe nothrow unittest +{ + import std.algorithm.comparison : equal; + + static struct S { + int v; + @disable this(this); + } + + immutable foo = [S(1), S(2), S(3)]; + auto r = retro(foo); + assert(equal(r, [S(3), S(2), S(1)])); +} /** Iterates range `r` with stride `n`. If the range is a @@ -585,7 +604,9 @@ do { @property void front(ElementType!R val) { - source.front = val; + import std.algorithm.mutation : move; + + source.front = move(val); } } @@ -864,6 +885,20 @@ pure @safe nothrow unittest assert(equal(s, [1L, 4L, 7L])); } +pure @safe nothrow unittest +{ + import std.algorithm.comparison : equal; + + static struct S { + int v; + @disable this(this); + } + + immutable foo = [S(1), S(2), S(3), S(4), S(5)]; + auto r = stride(foo, 3); + assert(equal(r, [S(1), S(4)])); +} + /** Spans multiple ranges in sequence. The function `chain` takes any number of ranges and returns a $(D Chain!(R1, R2,...)) object. The @@ -872,6 +907,11 @@ result is a range that offers the `front`, `popFront`, and $(D empty) primitives. If all input ranges offer random access and $(D length), `Chain` offers them as well. +Note that repeated random access of the resulting range is likely +to perform somewhat badly since lengths of the ranges in the chain have to be +added up for each random access operation. Random access to elements of +the first remaining range is still efficient. + If only one range is offered to `Chain` or `chain`, the $(D Chain) type exits the picture by aliasing itself directly to that range's type. @@ -907,7 +947,12 @@ if (Ranges.length > 0 && enum sameET = is(.ElementType!A == RvalueElementType); } - enum bool allSameType = allSatisfy!(sameET, R); + enum bool allSameType = allSatisfy!(sameET, R), + bidirectional = allSatisfy!(isBidirectionalRange, R), + mobileElements = allSatisfy!(hasMobileElements, R), + assignableElements = allSameType + && allSatisfy!(hasAssignableElements, R); + alias ElementType = RvalueElementType; static if (allSameType && allSatisfy!(hasLvalueElements, R)) @@ -925,17 +970,44 @@ if (Ranges.length > 0 && } } - // This is the entire state R source; - // TODO: use a vtable (or more) instead of linear iteration + size_t frontIndex; + // Always points to index one past the last non-empty range, + // because otherwise decrementing while pointing to first range + // would overflow to size_t.max. + static if (bidirectional) size_t backIndex; + else enum backIndex = source.length; public: this(R input) { - // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209 - static foreach (i, v; input) + frontIndex = source.length; + static if (bidirectional) backIndex = 0; + + foreach (i, ref v; input) source[i] = v; + + // We do this separately to avoid invoking `empty` needlessly. + // While not recommended, a range may depend on side effects of + // `empty` call. + foreach (i, ref v; input) if (!v.empty) { - source[i] = v; + frontIndex = i; + static if (bidirectional) backIndex = i+1; + break; + } + + // backIndex is already set in the first loop to + // as frontIndex+1, so we'll use that if we don't find a + // non-empty range here. + static if (bidirectional) + static foreach_reverse (i; 1 .. R.length + 1) + { + if (i <= frontIndex + 1) return; + if (!input[i-1].empty) + { + backIndex = i; + return; + } } } @@ -948,120 +1020,209 @@ if (Ranges.length > 0 && } else { - @property bool empty() + @property bool empty() => frontIndex >= backIndex; + } + + static if (allSatisfy!(isForwardRange, R)) + { + @property auto save() { - foreach (i, Unused; R) + auto saveI(size_t i)() => source[i].save; + + // TODO: this has the constructor needlessly refind + // frontIndex and backIndex. It'd be better to just copy + // those from `.this`. + auto saveResult = + Result(staticMap!(saveI, aliasSeqOf!(R.length.iota))); + + return saveResult; + } + } + + void popFront() + { + sw1: switch (frontIndex) + { + static foreach (i; 0 .. R.length) { - if (!source[i].empty) return false; + case i: + source[i].popFront(); + break sw1; } - return true; + + case R.length: + assert(0, "Attempt to `popFront` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - } - static if (allSatisfy!(isForwardRange, R)) - @property auto save() + sw2: switch (frontIndex) { - auto saveSource(size_t len)() + static foreach (i; 0 .. R.length) { - import std.typecons : tuple; - static assert(len > 0); - static if (len == 1) - { - return tuple(source[0].save); - } - else + case i: + if (source[i].empty) { - return saveSource!(len - 1)() ~ - tuple(source[len - 1].save); + frontIndex++; + goto case; } + else break sw2; } - return Result(saveSource!(R.length).expand); - } - void popFront() - { - foreach (i, Unused; R) - { - if (source[i].empty) continue; - source[i].popFront(); - return; + // Only possible to reach from goto of previous case. + case R.length: + break; + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to `popFront` of empty `chain` range"); } @property auto ref front() { - foreach (i, Unused; R) + switch (frontIndex) { - if (source[i].empty) continue; - return fixRef(source[i].front); + static foreach (i; 0 .. R.length) + { + case i: + return fixRef(source[i].front); + } + + case R.length: + assert(0, "Attempt to get `front` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to get `front` of empty `chain` range"); } - static if (allSameType && allSatisfy!(hasAssignableElements, R)) + static if (assignableElements) { // @@@BUG@@@ //@property void front(T)(T v) if (is(T : RvalueElementType)) @property void front(RvalueElementType v) { - foreach (i, Unused; R) + import std.algorithm.mutation : move; + + sw: switch (frontIndex) { - if (source[i].empty) continue; - source[i].front = v; - return; + static foreach (i; 0 .. R.length) + { + case i: + source[i].front = move(v); + break sw; + } + + case R.length: + assert(0, "Attempt to set `front` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to set `front` of empty `chain` range"); } } - static if (allSatisfy!(hasMobileElements, R)) + static if (mobileElements) { RvalueElementType moveFront() { - foreach (i, Unused; R) + switch (frontIndex) { - if (source[i].empty) continue; - return source[i].moveFront(); + static foreach (i; 0 .. R.length) + { + case i: + return source[i].moveFront(); + } + + case R.length: + assert(0, "Attempt to `moveFront` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to `moveFront` of empty `chain` range"); } } - static if (allSatisfy!(isBidirectionalRange, R)) + static if (bidirectional) { @property auto ref back() { - foreach_reverse (i, Unused; R) + switch (backIndex) { - if (source[i].empty) continue; - return fixRef(source[i].back); + static foreach_reverse (i; 1 .. R.length + 1) + { + case i: + return fixRef(source[i-1].back); + } + + case 0: + assert(0, "Attempt to get `back` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to get `back` of empty `chain` range"); } void popBack() { - foreach_reverse (i, Unused; R) + sw1: switch (backIndex) { - if (source[i].empty) continue; - source[i].popBack(); - return; + static foreach_reverse (i; 1 .. R.length + 1) + { + case i: + source[i-1].popBack(); + break sw1; + } + + case 0: + assert(0, "Attempt to `popFront` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); + } + + sw2: switch (backIndex) + { + static foreach_reverse (i; 1 .. R.length + 1) + { + case i: + if (source[i-1].empty) + { + backIndex--; + goto case; + } + else break sw2; + } + + // Only possible to reach from goto of previous case. + case 0: + break; + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to `popBack` of empty `chain` range"); } - static if (allSatisfy!(hasMobileElements, R)) + static if (mobileElements) { RvalueElementType moveBack() { - foreach_reverse (i, Unused; R) + switch (backIndex) { - if (source[i].empty) continue; - return source[i].moveBack(); + static foreach_reverse (i; 1 .. R.length + 1) + { + case i: + return source[i-1].moveBack(); + } + + case 0: + assert(0, "Attempt to `moveBack` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to `moveBack` of empty `chain` range"); } } @@ -1069,13 +1230,23 @@ if (Ranges.length > 0 && { @property void back(RvalueElementType v) { - foreach_reverse (i, Unused; R) + import std.algorithm.mutation : move; + + sw: switch (backIndex) { - if (source[i].empty) continue; - source[i].back = v; - return; + static foreach_reverse (i; 1 .. R.length + 1) + { + case i: + source[i-1].back = move(v); + break sw; + } + + case 0: + assert(0, "Attempt to set `back` of empty `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to set `back` of empty `chain` range"); } } } @@ -1084,11 +1255,24 @@ if (Ranges.length > 0 && { @property size_t length() { - size_t result; - foreach (i, Unused; R) + size_t result = 0; + sw: switch (frontIndex) { - result += source[i].length; + static foreach (i; 0 .. R.length) + { + case i: + result += source[i].length; + if (backIndex == i+1) break sw; + else goto case; + } + + case R.length: + break; + + default: + assert(0, "Internal library error. Please report it."); } + return result; } @@ -1099,64 +1283,93 @@ if (Ranges.length > 0 && { auto ref opIndex(size_t index) { - foreach (i, Range; R) + switch (frontIndex) { - static if (isInfinite!(Range)) - { - return source[i][index]; - } - else + static foreach (i; 0 .. R.length) { - immutable length = source[i].length; - if (index < length) return fixRef(source[i][index]); - index -= length; + case i: + static if (!isInfinite!(R[i])) + { + immutable length = source[i].length; + if (index >= length) + { + index -= length; + goto case; + } + } + + return fixRef(source[i][index]); } + + case R.length: + assert(0, "Attempt to access out-of-bounds index of `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to access out-of-bounds index of `chain` range"); } - static if (allSatisfy!(hasMobileElements, R)) + static if (mobileElements) { RvalueElementType moveAt(size_t index) { - foreach (i, Range; R) + switch (frontIndex) { - static if (isInfinite!(Range)) + static foreach (i; 0 .. R.length) { + case i: + static if (!isInfinite!(R[i])) + { + immutable length = source[i].length; + if (index >= length) + { + index -= length; + goto case; + } + } + return source[i].moveAt(index); } - else - { - immutable length = source[i].length; - if (index < length) return source[i].moveAt(index); - index -= length; - } + + case R.length: + assert(0, "Attempt to move out-of-bounds index of `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to move out-of-bounds index of `chain` range"); } } static if (allSameType && allSatisfy!(hasAssignableElements, R)) void opIndexAssign(ElementType v, size_t index) { - foreach (i, Range; R) + import std.algorithm.mutation : move; + + sw: switch (frontIndex) { - static if (isInfinite!(Range)) + static foreach (i; 0 .. R.length) { - source[i][index] = v; - } - else - { - immutable length = source[i].length; - if (index < length) + case i: + static if (!isInfinite!(R[i])) { - source[i][index] = v; - return; + immutable length = source[i].length; + if (index >= length) + { + index -= length; + goto case; + } } - index -= length; + + source[i][index] = move(v); + break sw; } + + case R.length: + assert(0, "Attempt to write out-of-bounds index of `chain` range"); + + default: + assert(0, "Internal library error. Please report it."); } - assert(false, "Attempt to write out-of-bounds index of `chain` range"); } } @@ -1164,40 +1377,74 @@ if (Ranges.length > 0 && auto opSlice(size_t begin, size_t end) return scope { auto result = this; - foreach (i, Unused; R) + + sw: switch (frontIndex) { - immutable len = result.source[i].length; - if (len < begin) - { - result.source[i] = result.source[i] - [len .. len]; - begin -= len; - } - else + static foreach (i; 0 .. R.length) { - result.source[i] = result.source[i] - [begin .. len]; - break; + case i: + immutable len = result.source[i].length; + if (len <= begin) + { + result.source[i] = result.source[i] + [len .. len]; + begin -= len; + result.frontIndex++; + goto case; + } + else + { + result.source[i] = result.source[i] + [begin .. len]; + break sw; + } } + + case R.length: + assert(begin == 0, + "Attempt to access out-of-bounds slice of `chain` range"); + break; + + default: + assert(0, "Internal library error. Please report it."); } - auto cut = length; - cut = cut <= end ? 0 : cut - end; - foreach_reverse (i, Unused; R) + + // Overflow intentional if end index too big. + // This will trigger the bounds check failure below. + auto cut = length - end; + + sw2: switch (backIndex) { - immutable len = result.source[i].length; - if (cut > len) + static foreach_reverse (i; 1 .. R.length + 1) { - result.source[i] = result.source[i] - [0 .. 0]; - cut -= len; - } - else - { - result.source[i] = result.source[i] - [0 .. len - cut]; - break; + case i: + immutable len = result.source[i-1].length; + if (len <= cut) + { + result.source[i-1] = result.source[i-1] + [0 .. 0]; + cut -= len; + result.backIndex--; + goto case; + } + else + { + result.source[i-1] = result.source[i-1] + [0 .. len - cut]; + break sw2; + } } + + case 0: + assert(cut == 0, end > length? + "Attempt to access out-of-bounds slice of `chain` range": + "Attempt to access negative length slice of `chain` range"); + break sw2; + + default: + assert(0, "Internal library error. Please report it."); } + return result; } } @@ -1293,6 +1540,10 @@ pure @safe nothrow unittest auto s = chain(arr1, arr2, arr3); assert(s[5] == 6); assert(equal(s, witness)); + assert(s[4 .. 6].equal(arr2)); + assert(s[2 .. 5].equal([3, 4, 5])); + assert(s[0 .. 0].empty); + assert(s[7 .. $].empty); assert(s[5] == 6); } { @@ -1392,6 +1643,28 @@ pure @safe unittest assert(equal(r, "foobar")); } +pure @safe nothrow @nogc unittest +{ + // support non-copyable items + + static struct S { + int v; + @disable this(this); + } + + S[2] s0, s1; + foreach (ref el; chain(s0[], s1[])) + { + int n = el.v; + } + + S[] s2, s3; + foreach (ref el; chain(s2, s3)) + { + int n = el.v; + } +} + /** Choose one of two ranges at runtime depending on a Boolean condition. @@ -1825,6 +2098,23 @@ pure @safe unittest auto chosen2 = chosen.save; } +pure @safe nothrow unittest +{ + static struct S { + int v; + @disable this(this); + } + + auto a = [S(1), S(2), S(3)]; + auto b = [S(4), S(5), S(6)]; + + auto chosen = choose(true, a, b); + assert(chosen.front.v == 1); + + auto chosen2 = choose(false, a, b); + assert(chosen2.front.v == 4); +} + /** Choose one of multiple ranges at runtime. @@ -2122,6 +2412,20 @@ pure @safe unittest assert(equal(r.save, "fboaor")); assert(equal(r.save, "fboaor")); } +pure @safe nothrow unittest +{ + import std.algorithm.comparison : equal; + + static struct S { + int v; + @disable this(this); + } + + S[] a = [ S(1), S(2) ]; + S[] b = [ S(10), S(20) ]; + auto r = roundRobin(a, b); + assert(equal(r, [ S(1), S(10), S(2), S(20) ])); +} /** Iterates a random-access range starting from a given point and @@ -2313,12 +2617,12 @@ if (isInputRange!(Unqual!Range) && /// ditto @property void front(ElementType!R v) { + import std.algorithm.mutation : move; + assert(!empty, "Attempting to assign to the front of an empty " ~ Take.stringof); - // This has to return auto instead of void because of - // https://issues.dlang.org/show_bug.cgi?id=4706 - source.front = v; + source.front = move(v); } static if (hasMobileElements!R) @@ -2694,10 +2998,12 @@ if (isInputRange!R) { @property auto ref front(ElementType!R v) { + import std.algorithm.mutation : move; + assert(!empty, "Attempting to assign to the front of an empty " ~ typeof(this).stringof); - return _input.front = v; + return _input.front = move(v); } } } @@ -3929,7 +4235,9 @@ if (isForwardRange!R && !isInfinite!R) /// ditto @property void front(ElementType!R val) { - _original[_index] = val; + import std.algorithm.mutation : move; + + _original[_index] = move(val); } } @@ -4039,7 +4347,9 @@ if (isForwardRange!R && !isInfinite!R) /// ditto @property auto front(ElementType!R val) { - return _current.front = val; + import std.algorithm.mutation : move; + + return _current.front = move(val); } } @@ -7053,7 +7363,9 @@ struct FrontTransversal(Ror, { @property void front(ElementType val) { - _input.front.front = val; + import std.algorithm.mutation : move; + + _input.front.front = move(val); } } @@ -7110,7 +7422,9 @@ struct FrontTransversal(Ror, { @property void back(ElementType val) { - _input.back.front = val; + import std.algorithm.mutation : move; + + _input.back.front = move(val); } } } @@ -7143,7 +7457,9 @@ struct FrontTransversal(Ror, { void opIndexAssign(ElementType val, size_t n) { - _input[n].front = val; + import std.algorithm.mutation : move; + + _input[n].front = move(val); } } mixin ImplementLength!_input; @@ -8785,7 +9101,8 @@ public: { // `nextSource` is used to "look one step into the future" and check for the end // this means `nextSource` is advanced by `stepSize` on every `popFront` - nextSource = source.save.drop(windowSize); + nextSource = source.save; + auto poppedElems = nextSource.popFrontN(windowSize); } if (source.empty) @@ -8798,7 +9115,7 @@ public: { static if (needsEndTracker) { - if (nextSource.empty) + if (poppedElems < windowSize) hasShownPartialBefore = true; } else @@ -8806,14 +9123,13 @@ public: if (source.length <= windowSize) hasShownPartialBefore = true; } - } else { // empty source range is needed, s.t. length, slicing etc. works properly static if (needsEndTracker) { - if (nextSource.empty) + if (poppedElems < windowSize) _empty = true; } else @@ -9770,6 +10086,15 @@ public: assert([1].map!(x => x).slide(2).equal!equal([[1]])); } +// https://issues.dlang.org/show_bug.cgi?id=19642 +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : splitter; + + assert("ab cd".splitter(' ').slide!(No.withPartial)(2).equal!equal([["ab", "cd"]])); +} + private struct OnlyResult(Values...) if (Values.length > 1) { diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d index 748fde3..e581409 100644 --- a/libphobos/src/std/range/primitives.d +++ b/libphobos/src/std/range/primitives.d @@ -1041,7 +1041,8 @@ See_Also: */ enum bool isBidirectionalRange(R) = isForwardRange!R && is(typeof((R r) => r.popBack)) - && is(typeof((R r) { return r.back; } (R.init)) == ElementType!R); + && (is(typeof((return ref R r) => r.back)) || is(typeof(ref (return ref R r) => r.back))) + && is(typeof(R.init.back.init) == ElementType!R); /// @safe unittest diff --git a/libphobos/src/std/regex/internal/backtracking.d b/libphobos/src/std/regex/internal/backtracking.d index 21766fc..ac73f70 100644 --- a/libphobos/src/std/regex/internal/backtracking.d +++ b/libphobos/src/std/regex/internal/backtracking.d @@ -73,12 +73,7 @@ final: override @property ref size_t refCount() { return _refCount; } override @property ref const(RegEx) pattern(){ return re; } - static if (__traits(hasMember,Stream, "search")) - { - enum kicked = true; - } - else - enum kicked = false; + enum kicked = __traits(hasMember, Stream, "search"); static size_t initialMemory(const ref RegEx re) { diff --git a/libphobos/src/std/regex/internal/thompson.d b/libphobos/src/std/regex/internal/thompson.d index ab28d37..f4643ae 100644 --- a/libphobos/src/std/regex/internal/thompson.d +++ b/libphobos/src/std/regex/internal/thompson.d @@ -759,12 +759,7 @@ final: } - static if (__traits(hasMember,Stream, "search")) - { - enum kicked = true; - } - else - enum kicked = false; + enum kicked = __traits(hasMember, Stream, "search"); static size_t getThreadSize(const ref Regex!Char re) { diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index 92e4906..5ed685f 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -43,6 +43,8 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP digitalmars.com, Walter Bright), $(HTTP erdani.org, Andrei Alexandrescu), Alex Rønne Petersen +Macros: +CSTDIO=$(HTTP cplusplus.com/reference/cstdio/$1/, $1) */ module std.stdio; @@ -534,8 +536,7 @@ Params: name = range or string representing the file _name stdioOpenmode = range or string represting the open mode (with the same semantics as in the C standard library - $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) - function) + $(CSTDIO fopen) function) Throws: `ErrnoException` if the file could not be opened. */ @@ -618,8 +619,7 @@ file. /** Detaches from the current file (throwing on failure), and then attempts to _open file `name` with mode `stdioOpenmode`. The mode has the -same semantics as in the C standard library $(HTTP -cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function. +same semantics as in the C standard library $(CSTDIO fopen) function. Throws: `ErrnoException` in case of error. */ @@ -735,8 +735,7 @@ Reuses the `File` object to either open a different file, or change the file mode. If `name` is `null`, the mode of the currently open file is changed; otherwise, a new file is opened, reusing the C `FILE*`. The function has the same semantics as in the C standard -library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen) -function. +library $(CSTDIO freopen) function. Note: Calling `reopen` with a `null` `name` is not implemented in all C runtimes. @@ -832,8 +831,8 @@ Throws: `ErrnoException` in case of error. Params: fd = File descriptor to associate with this `File`. stdioOpenmode = Mode to associate with this File. The mode has the same semantics - semantics as in the C standard library - $(HTTP cplusplus.com/reference/cstdio/fopen/, fdopen) function, and must be compatible with `fd`. + semantics as in the C standard library $(CSTDIO fdopen) function, + and must be compatible with `fd`. */ void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe { @@ -932,8 +931,7 @@ Throws: `ErrnoException` in case of error. } /** -Returns `true` if the file is at end (see $(HTTP -cplusplus.com/reference/clibrary/cstdio/feof.html, feof)). +Returns `true` if the file is at end (see $(CSTDIO feof)). Throws: `Exception` if the file is not opened. */ @@ -961,8 +959,7 @@ Throws: `Exception` if the file is not opened. /** If the file is closed or not yet opened, returns `true`. Otherwise, returns -$(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for -the file handle. +$(CSTDIO ferror) for the file handle. */ @property bool error() const @trusted pure nothrow { @@ -1017,8 +1014,7 @@ Throws: `ErrnoException` on failure if closing the file. /** If the file was closed or not yet opened, succeeds vacuously. Otherwise -closes the file (by calling $(HTTP -cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)), +closes the file (by calling $(CSTDIO fclose)), throwing on error. Even if an exception is thrown, afterwards the $(D File) object is empty. This is different from `detach` in that it always closes the file; consequently, all other `File` objects @@ -1046,8 +1042,7 @@ Throws: `ErrnoException` on error. /** If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns -$(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html, -_clearerr) for the file handle. +$(CSTDIO clearerr) for the file handle. */ void clearerr() @safe pure nothrow { @@ -1058,8 +1053,7 @@ _clearerr) for the file handle. /** Flushes the C `FILE` buffers. -Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush) -for the file handle. +Calls $(CSTDIO fflush) for the file handle. Throws: `Exception` if the file is not opened or if the call to `fflush` fails. */ @@ -1125,7 +1119,7 @@ Throws: `Exception` if the file is not opened or if the OS call fails. } /** -Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the +Calls $(CSTDIO fread) for the file handle. The number of items to read and the size of each item is inferred from the size and type of the input array, respectively. @@ -1220,7 +1214,7 @@ Throws: `ErrnoException` if the file is not opened or the call to `fread` fails. } /** -Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file +Calls $(CSTDIO fwrite) for the file handle. The number of items to write and the size of each item is inferred from the size and type of the input array, respectively. An error is thrown if the buffer could not be written in its entirety. @@ -1290,7 +1284,7 @@ Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fa } /** -Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek) +Calls $(CSTDIO fseek) for the file handle to move its position indicator. Params: @@ -1374,7 +1368,7 @@ Throws: `Exception` if the file is not opened. } /** -Calls $(HTTP cplusplus.com/reference/cstdio/ftell.html, ftell) +Calls $(CSTDIO ftell) for the managed file handle, which returns the current value of the position indicator of the file handle. @@ -1420,8 +1414,7 @@ Throws: `Exception` if the file is not opened. } /** -Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind) -for the file handle. +Calls $(CSTDIO rewind) for the file handle. Throws: `Exception` if the file is not opened. */ @@ -1434,8 +1427,7 @@ Throws: `Exception` if the file is not opened. } /** -Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for -the file handle. +Calls $(CSTDIO setvbuf) for the file handle. Throws: `Exception` if the file is not opened. `ErrnoException` if the call to `setvbuf` fails. @@ -1450,8 +1442,7 @@ Throws: `Exception` if the file is not opened. } /** -Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, -_setvbuf) for the file handle. +Calls $(CSTDIO setvbuf) for the file handle. Throws: `Exception` if the file is not opened. `ErrnoException` if the call to `setvbuf` fails. @@ -2252,8 +2243,7 @@ $(CONSOLE } /** - Returns a temporary file by calling - $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile). + Returns a temporary file by calling $(CSTDIO tmpfile). Note that the created file has no $(LREF name).*/ static File tmpfile() @safe { diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index f4d011bb..689f0ae 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -1198,7 +1198,7 @@ if (isCallable!func) } /** -Convert the result of `__traits(getParameterStorageClasses)` +Convert the result of $(DDSUBLINK spec/traits, getParameterStorageClasses, `__traits(getParameterStorageClasses)`) to $(LREF ParameterStorageClass) `enum`s. Params: @@ -2184,7 +2184,7 @@ if (isCallable!func) void func() {} static assert(variadicFunctionStyle!func == Variadic.no); - extern(C) int printf(in char*, ...); + extern(C) int printf(const char*, ...); static assert(variadicFunctionStyle!printf == Variadic.c); } @@ -2572,6 +2572,8 @@ if (is(T == class)) /** Determines whether `T` has its own context pointer. `T` must be either `class`, `struct`, or `union`. + +See also: $(DDSUBLINK spec/traits, isNested, `__traits(isNested, T)`) */ template isNested(T) if (is(T == class) || is(T == struct) || is(T == union)) @@ -3857,6 +3859,8 @@ package alias Identity(alias A) = A; /** Yields `true` if and only if `T` is an aggregate that defines a symbol called `name`. + + See also: $(DDSUBLINK spec/traits, hasMember, `__traits(hasMember, T, name)`) */ enum hasMember(T, string name) = __traits(hasMember, T, name); @@ -4839,6 +4843,8 @@ package enum maxAlignment(U...) = /** Returns class instance alignment. + +See also: $(DDSUBLINK spec/traits, classInstanceAlignment, `__traits(classInstanceAlignment, T)`) */ template classInstanceAlignment(T) if (is(T == class)) @@ -5313,7 +5319,7 @@ enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lv } else { - mixin(q{ struct S6 { void opAssign(in ref S5); } }); + mixin(q{ struct S6 { void opAssign(scope const ref S5); } }); static assert(!isRvalueAssignable!(S6, S5)); static assert( isLvalueAssignable!(S6, S5)); @@ -5680,7 +5686,7 @@ private struct __InoutWorkaroundStruct{} /** Creates an lvalue or rvalue of type `T` for `typeof(...)` and -`__traits(compiles, ...)` purposes. No actual value is returned. +$(DDSUBLINK spec/traits, compiles, `__traits(compiles, ...)`) purposes. No actual value is returned. Params: T = The type to transform @@ -6166,7 +6172,7 @@ template BuiltinTypeOf(T) //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// /** - * Detect whether `T` is a built-in boolean type. + * Detect whether `T` is a built-in boolean type or enum of boolean base type. */ enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool); @@ -6196,8 +6202,15 @@ enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool); } /** - * Detect whether `T` is a built-in integral type. Types `bool`, - * `char`, `wchar`, and `dchar` are not considered integral. + * Detect whether `T` is a built-in integral type. + * Integral types are `byte`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `cent`, `ucent`, + * and enums with an integral type as its base type. + * Params: + * T = type to test + * Returns: + * `true` if `T` is an integral type + * Note: + * this is not the same as $(LINK2 https://dlang.org/spec/traits.html#isIntegral, `__traits(isIntegral)`) */ template isIntegral(T) { @@ -6260,7 +6273,10 @@ template isIntegral(T) /** * Detect whether `T` is a built-in floating point type. + * + * See also: $(DDSUBLINK spec/traits, isFloating, `__traits(isFloating, T)`) */ +// is(T : real) to discount complex types enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real); /// @@ -6398,7 +6414,10 @@ template isNumeric(T) /** * Detect whether `T` is a scalar type (a built-in numeric, character or * boolean type). + * + * See also: $(DDSUBLINK spec/traits, isScalar, `__traits(isScalar, T)`) */ +// is(T : real) to discount complex types enum bool isScalarType(T) = __traits(isScalar, T) && is(T : real); /// @@ -6927,6 +6946,8 @@ template isAutodecodableString(T) /** * Detect whether type `T` is a static array. + * + * See also: $(DDSUBLINK spec/traits, isStaticArray, `__traits(isStaticArray, T)`) */ enum bool isStaticArray(T) = __traits(isStaticArray, T); @@ -7056,6 +7077,8 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; /** * Detect whether `T` is an associative array type + * + * See also: $(DDSUBLINK spec/traits, isAssociativeArray, `__traits(isAssociativeArray, T)`) */ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); @@ -7542,14 +7565,12 @@ template isCallable(alias callable) else static if (is(typeof(&callable.opCall) V : V*) && is(V == function)) // T is a type which has a static member function opCall(). enum bool isCallable = true; - else static if (is(typeof(&callable.opCall!()))) + else static if (is(typeof(&callable.opCall!()) TemplateInstanceType)) { - alias TemplateInstanceType = typeof(&callable.opCall!()); enum bool isCallable = isCallable!TemplateInstanceType; } - else static if (is(typeof(&callable!()))) + else static if (is(typeof(&callable!()) TemplateInstanceType)) { - alias TemplateInstanceType = typeof(&callable!()); enum bool isCallable = isCallable!TemplateInstanceType; } else @@ -7613,14 +7634,15 @@ template isCallable(alias callable) /** -Detect whether `T` is an abstract function. +Detect whether `S` is an abstract function. +See also: $(DDSUBLINK spec/traits, isAbstractFunction, `__traits(isAbstractFunction, S)`) Params: - T = The type to check + S = The symbol to check Returns: A `bool` */ -enum isAbstractFunction(alias T) = __traits(isAbstractFunction, T); +enum isAbstractFunction(alias S) = __traits(isAbstractFunction, S); /// @safe unittest @@ -7635,9 +7657,11 @@ enum isAbstractFunction(alias T) = __traits(isAbstractFunction, T); } /** - * Detect whether `T` is a final function. + * Detect whether `S` is a final function. + * + * See also: $(DDSUBLINK spec/traits, isFinalFunction, `__traits(isFinalFunction, S)`) */ -enum isFinalFunction(alias T) = __traits(isFinalFunction, T); +enum isFinalFunction(alias S) = __traits(isFinalFunction, S); /// @safe unittest @@ -7703,9 +7727,11 @@ template isNestedFunction(alias f) } /** - * Detect whether `T` is an abstract class. + * Detect whether `S` is an abstract class. + * + * See also: $(DDSUBLINK spec/traits, isAbstractClass, `__traits(isAbstractClass, S)`) */ -enum isAbstractClass(alias T) = __traits(isAbstractClass, T); +enum isAbstractClass(alias S) = __traits(isAbstractClass, S); /// @safe unittest @@ -7723,9 +7749,11 @@ enum isAbstractClass(alias T) = __traits(isAbstractClass, T); } /** - * Detect whether `T` is a final class. + * Detect whether `S` is a final class. + * + * See also: $(DDSUBLINK spec/traits, isFinalClass, `__traits(isFinalClass, S)`) */ -enum isFinalClass(alias T) = __traits(isFinalClass, T); +enum isFinalClass(alias S) = __traits(isFinalClass, S); /// @safe unittest @@ -9092,12 +9120,13 @@ template isFinal(alias X) + If a type cannot be copied, then code such as `MyStruct x; auto y = x;` will fail to compile. + Copying for structs can be disabled by using `@disable this(this)`. + + + See also: $(DDSUBLINK spec/traits, isCopyable, `__traits(isCopyable, S)`) + Params: + S = The type to check. + + Returns: + `true` if `S` can be copied. `false` otherwise. - + ++/ + +/ enum isCopyable(S) = __traits(isCopyable, S); /// diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index bde8439..d267e71 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -2332,7 +2332,7 @@ break the soundness of D's type system and does not incur any of the risks usually associated with `cast`. Params: - T = An object, interface, array slice type, or associative array type. + T = Any type. */ template Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T) @@ -2395,15 +2395,155 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr static assert(!__traits(compiles, &r.get())); } +/// ditto +struct Rebindable(T) +if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T) +{ +private: + static if (isAssignable!(typeof(cast() T.init))) + { + enum useQualifierCast = true; + + typeof(cast() T.init) data; + } + else + { + enum useQualifierCast = false; + + align(T.alignof) + static struct Payload + { + static if (hasIndirections!T) + { + void[T.sizeof] data; + } + else + { + ubyte[T.sizeof] data; + } + } + + Payload data; + } + +public: + + static if (!__traits(compiles, { T value; })) + { + @disable this(); + } + + /** + * Constructs a `Rebindable` from a given value. + */ + this(T value) @trusted + { + static if (useQualifierCast) + { + this.data = cast() value; + } + else + { + set(value); + } + } + + /** + * Overwrites the currently stored value with `value`. + */ + void opAssign(this This)(T value) @trusted + { + clear; + set(value); + } + + /** + * Returns the value currently stored in the `Rebindable`. + */ + T get(this This)() @property @trusted + { + static if (useQualifierCast) + { + return cast(T) this.data; + } + else + { + return *cast(T*) &this.data; + } + } + + static if (!useQualifierCast) + { + ~this() @trusted + { + clear; + } + } + + /// + alias get this; + +private: + + void set(this This)(T value) + { + static if (useQualifierCast) + { + this.data = cast() value; + } + else + { + // As we're escaping a copy of `value`, deliberately leak a copy: + static union DontCallDestructor + { + T value; + } + DontCallDestructor copy = DontCallDestructor(value); + this.data = *cast(Payload*) © + } + } + + void clear(this This)() + { + // work around reinterpreting cast being impossible in CTFE + if (__ctfe) + { + return; + } + + // call possible struct destructors + .destroy!(No.initialize)(*cast(T*) &this.data); + } +} + +/// Using Rebindable in a generic algorithm: @safe unittest { - class CustomToHash + import std.range.primitives : front, popFront; + + // simple version of std.algorithm.searching.maxElement + typeof(R.init.front) maxElement(R)(R r) { - override size_t toHash() const nothrow @trusted { return 42; } + auto max = rebindable(r.front); + r.popFront; + foreach (e; r) + if (e > max) + max = e; // Rebindable allows const-correct reassignment + return max; } - Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash(); - assert(a.toHash() == 42, "Rebindable!A should offer toHash()" - ~ " by forwarding to A.toHash()."); + struct S + { + char[] arr; + alias arr this; // for comparison + } + // can't convert to mutable + const S cs; + static assert(!__traits(compiles, { S s = cs; })); + + alias CS = const S; + CS[] arr = [CS("harp"), CS("apple"), CS("pot")]; + CS ms = maxElement(arr); + assert(ms.arr == "pot"); } // https://issues.dlang.org/show_bug.cgi?id=18615 @@ -2453,6 +2593,34 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr ~ " comparable against Object itself and use Object.opEquals."); } +/// +@system unittest +{ + static struct S + { + int* ptr; + } + S s = S(new int); + + const cs = s; + // Can't assign s.ptr to cs.ptr + static assert(!__traits(compiles, {s = cs;})); + + Rebindable!(const S) rs = s; + assert(rs.ptr is s.ptr); + // rs.ptr is const + static assert(!__traits(compiles, {rs.ptr = null;})); + + // Can't assign s.ptr to rs.ptr + static assert(!__traits(compiles, {s = rs;})); + + const S cs2 = rs; + // Rebind rs + rs = cs2; + rs = S(); + assert(rs.ptr is null); +} + // https://issues.dlang.org/show_bug.cgi?id=18755 @safe unittest { @@ -2473,13 +2641,145 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr })); } +@safe unittest +{ + class CustomToHash + { + override size_t toHash() const nothrow @trusted { return 42; } + } + Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash(); + assert(a.toHash() == 42, "Rebindable!A should offer toHash()" + ~ " by forwarding to A.toHash()."); +} + +// Test Rebindable!immutable +@safe unittest +{ + static struct S + { + int* ptr; + } + S s = S(new int); + + Rebindable!(immutable S) ri = S(new int); + assert(ri.ptr !is null); + static assert(!__traits(compiles, {ri.ptr = null;})); + + // ri is not compatible with mutable S + static assert(!__traits(compiles, {s = ri;})); + static assert(!__traits(compiles, {ri = s;})); + + auto ri2 = ri; + assert(ri2.ptr == ri.ptr); + + const S cs3 = ri; + static assert(!__traits(compiles, {ri = cs3;})); + + immutable S si = ri; + // Rebind ri + ri = si; + ri = S(); + assert(ri.ptr is null); + + // Test RB!immutable -> RB!const + Rebindable!(const S) rc = ri; + assert(rc.ptr is null); + ri = S(new int); + rc = ri; + assert(rc.ptr !is null); + + // test rebindable, opAssign + rc.destroy; + assert(rc.ptr is null); + rc = rebindable(cs3); + rc = rebindable(si); + assert(rc.ptr !is null); + + ri.destroy; + assert(ri.ptr is null); + ri = rebindable(si); + assert(ri.ptr !is null); +} + +// Test disabled default ctor +@safe unittest +{ + static struct ND + { + int i; + @disable this(); + this(int i) inout {this.i = i;} + } + static assert(!__traits(compiles, Rebindable!ND())); + + Rebindable!(const ND) rb = const ND(1); + assert(rb.i == 1); + rb = immutable ND(2); + assert(rb.i == 2); + rb = rebindable(const ND(3)); + assert(rb.i == 3); + static assert(!__traits(compiles, rb.i++)); +} + +// Test copying +@safe unittest +{ + int del; + int post; + struct S + { + int* ptr; + int level; + this(this) + { + post++; + level++; + } + ~this() + { + del++; + } + } + + // test ref count + { + Rebindable!S rc = S(new int); + } + assert(post == del - 1); +} + +@safe unittest +{ + int del; + int post; + struct S + { + immutable int x; + int level; + this(this) + { + post++; + level++; + } + ~this() + { + del++; + } + } + + // test ref count + { + Rebindable!S rc = S(0); + } + assert(post == del - 1); +} + /** Convenience function for creating a `Rebindable` using automatic type inference. Params: - obj = A reference to an object, interface, associative array, or an array slice - to initialize the `Rebindable` with. + obj = A reference to a value to initialize the `Rebindable` with. Returns: A newly constructed `Rebindable` initialized with the given reference. @@ -2514,6 +2814,26 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr assert(c3.payload == 2); } +/// ditto +Rebindable!T rebindable(T)(T value) +if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T + && !is(T : Rebindable!U, U)) +{ + return Rebindable!T(value); +} + +/// +@safe unittest +{ + immutable struct S + { + int[] array; + } + auto s1 = [3].idup.rebindable; + s1 = [4].idup.rebindable; + assert(s1 == [4]); +} + /** This function simply returns the `Rebindable` object passed in. It's useful in generic programming cases when a given object may be either a regular @@ -2626,10 +2946,6 @@ Rebindable!T rebindable(T)(Rebindable!T obj) static assert(is(Rebindable!(T[]) == T[])); } - // https://issues.dlang.org/show_bug.cgi?id=12046 - static assert(!__traits(compiles, Rebindable!(int[1]))); - static assert(!__traits(compiles, Rebindable!(const int[1]))); - // Pull request 3341 Rebindable!(immutable int[int]) pr3341 = [123:345]; assert(pr3341[123] == 345); |