From e67a05138e77f39a551da78712a4da5c7d4c702b Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 19 Nov 2018 21:37:58 +0100 Subject: [PATCH] range.primitive: Support non copyable elements ... in all ranges. --- source/tanya/algorithm/iteration.d | 8 +-- source/tanya/algorithm/mutation.d | 5 +- source/tanya/conv.d | 22 +++----- source/tanya/memory/smartref.d | 15 ++---- source/tanya/range/primitive.d | 80 ++++++++++++++++++++++++++---- source/tanya/test/stub.d | 39 ++++++++++++++- source/tanya/typecons.d | 22 ++++---- 7 files changed, 133 insertions(+), 58 deletions(-) diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index 9ad8cdd..bfd1f13 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -3,13 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Range adapters. + * Iteration algorithms. * - * A range adapter wraps another range and modifies the way, how the original + * These algorithms wrap other ranges and modify the way, how the original * range is iterated, or the order in which its elements are accessed. * - * All adapters are lazy algorithms, they request the next element of the - * adapted range on demand. + * All algorithms in this module are lazy, they request the next element of the + * original range on demand. * * Copyright: Eugene Wissner 2018. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d index 9c05826..bd71f40 100644 --- a/source/tanya/algorithm/mutation.d +++ b/source/tanya/algorithm/mutation.d @@ -19,6 +19,7 @@ static import tanya.memory.op; import tanya.meta.trait; import tanya.meta.transform; import tanya.range; +version (unittest) import tanya.test.stub; private void deinitialize(bool zero, T)(ref T value) { @@ -554,10 +555,6 @@ if (isInputRange!Range && hasLvalueElements!Range) @nogc nothrow pure @safe unittest { - static struct NonCopyable - { - @disable this(this); - } NonCopyable[] nonCopyable; initializeAll(nonCopyable); } diff --git a/source/tanya/conv.d b/source/tanya/conv.d index e19ea7f..97fafa4 100644 --- a/source/tanya/conv.d +++ b/source/tanya/conv.d @@ -27,6 +27,7 @@ import tanya.range.primitive; version (unittest) { import tanya.test.assertion; + import tanya.test.stub; } /** @@ -257,32 +258,23 @@ out(result; memory.ptr is result) // Can emplace structs without a constructor @nogc nothrow pure @safe unittest { - static struct SWithDtor - { - ~this() @nogc nothrow pure @safe - { - } - } - static assert(is(typeof(emplace!SWithDtor(null, SWithDtor())))); - static assert(is(typeof(emplace!SWithDtor(null)))); + static assert(is(typeof(emplace!WithDtor(null, WithDtor())))); + static assert(is(typeof(emplace!WithDtor(null)))); } // Doesn't call a destructor on uninitialized elements @nogc nothrow pure @system unittest { - static struct WithDtor + static struct SWithDtor { private bool canBeInvoked = false; ~this() @nogc nothrow pure @safe { - if (!this.canBeInvoked) - { - assert(false); - } + assert(this.canBeInvoked); } } - void[WithDtor.sizeof] memory = void; - auto actual = emplace!WithDtor(memory[], WithDtor(true)); + void[SWithDtor.sizeof] memory = void; + auto actual = emplace!SWithDtor(memory[], SWithDtor(true)); assert(actual.canBeInvoked); } diff --git a/source/tanya/memory/smartref.d b/source/tanya/memory/smartref.d index 962752f..020627d 100644 --- a/source/tanya/memory/smartref.d +++ b/source/tanya/memory/smartref.d @@ -30,6 +30,7 @@ import tanya.exception; import tanya.memory; import tanya.meta.trait; import tanya.range.primitive; +version (unittest) import tanya.test.stub; private template Payload(T) { @@ -611,19 +612,11 @@ do @nogc @system unittest { - static bool destroyed; - - static struct F + size_t destroyed; { - ~this() @nogc nothrow @safe - { - destroyed = true; - } + auto rc = defaultAllocator.refCounted!WithDtor(destroyed); } - { - auto rc = defaultAllocator.refCounted!F(); - } - assert(destroyed); + assert(destroyed == 1); } /** diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index 099208a..8cf45f2 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -316,6 +316,26 @@ private template isDynamicArrayRange(R) } } +private struct Primitive(Candidate, string primitive) +{ + auto ref returnType(Candidate candidate) + { + mixin("return candidate." ~ primitive ~ ";"); + } + + alias ReturnType = .ReturnType!returnType; + static assert(!is(ReturnType == void)); + + enum uint attributes = functionAttributes!returnType + & FunctionAttribute.ref_; + + bool opEquals(That)(That) const + { + return is(ReturnType == That.ReturnType) + && attributes == That.attributes; + } +} + /** * Determines whether $(D_PARAM R) is an input range. * @@ -335,11 +355,11 @@ private template isDynamicArrayRange(R) */ template isInputRange(R) { - static if (is(ReturnType!((R r) => r.front()) U) + static if (is(Primitive!(R, "front()") U) && is(ReturnType!((R r) => r.empty) == bool) && is(typeof(R.popFront()))) { - enum bool isInputRange = !is(U == void); + enum bool isInputRange = true; } else { @@ -415,6 +435,28 @@ template isInputRange(R) static assert(isInputRange!Range4); } +// Ranges with non-copyable elements can be input ranges +@nogc nothrow pure @safe unittest +{ + @WithLvalueElements + static struct R + { + mixin InputRangeStub!NonCopyable; + } + static assert(isInputRange!R); +} + +// Ranges with const non-copyable elements can be input ranges +@nogc nothrow pure @safe unittest +{ + @WithLvalueElements + static struct R + { + mixin InputRangeStub!(const(NonCopyable)); + } + static assert(isInputRange!R); +} + /** * Determines whether $(D_PARAM R) is a forward range. * @@ -521,11 +563,11 @@ template isForwardRange(R) */ template isBidirectionalRange(R) { - static if (is(ReturnType!((R r) => r.back()) U) + static if (is(Primitive!(R, "back()") U) && is(typeof(R.popBack()))) { enum bool isBidirectionalRange = isForwardRange!R - && is(U == ReturnType!((R r) => r.front())); + && (U() == Primitive!(R, "front()")()); } else { @@ -591,6 +633,17 @@ template isBidirectionalRange(R) static assert(!isBidirectionalRange!(Range!(int, const int))); } +// Ranges with non-copyable elements can be bidirectional ranges +@nogc nothrow pure @safe unittest +{ + @WithLvalueElements + static struct R + { + mixin BidirectionalRangeStub!NonCopyable; + } + static assert(isBidirectionalRange!R); +} + /** * Determines whether $(D_PARAM R) is a random-access range. * @@ -616,11 +669,11 @@ template isBidirectionalRange(R) */ template isRandomAccessRange(R) { - static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U)) + static if (is(Primitive!(R, "opIndex(size_t.init)") U)) { enum bool isRandomAccessRange = isInputRange!R && (hasLength!R || isInfinite!R) - && is(U == ReturnType!((R r) => r.front())); + && (U() == Primitive!(R, "front()")()); } else { @@ -731,6 +784,17 @@ template isRandomAccessRange(R) static assert(!isRandomAccessRange!Range4); } +// Ranges with non-copyable elements can be random-access ranges +@nogc nothrow pure @safe unittest +{ + @WithLvalueElements @Infinite + static struct R + { + mixin RandomAccessRangeStub!NonCopyable; + } + static assert(isRandomAccessRange!R); +} + /** * Puts $(D_PARAM e) into the $(D_PARAM range). * @@ -1698,10 +1762,6 @@ template hasLvalueElements(R) // Works with non-copyable elements @nogc nothrow pure @safe unittest { - static struct NonCopyable - { - @disable this(this); - } static assert(hasLvalueElements!(NonCopyable[])); } diff --git a/source/tanya/test/stub.d b/source/tanya/test/stub.d index 2be252e..234e409 100644 --- a/source/tanya/test/stub.d +++ b/source/tanya/test/stub.d @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Range generators. + * Range and generic type generators. * * Copyright: Eugene Wissner 2018. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, @@ -248,6 +248,9 @@ mixin template BidirectionalRangeStub(E = int) * This mixin includes input range primitives as well, but can be combined with * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). * + * Note that a random-access range also requires $(D_PSYMBOL Length) or + * $(D_PARAM Infinite) by definition. + * * Params: * E = Element type. */ @@ -273,3 +276,37 @@ mixin template RandomAccessRangeStub(E = int) } } } + +/** + * Struct with a disabled postblit constructor. + */ +struct NonCopyable +{ + @disable this(this); +} + +/** + * Struct with an elaborate destructor. + * + * The constructor of $(D_PSYMBOL WithDtor) accepts an additional `counter` + * argument, which is incremented by the destructor. $(D_PSYMBOL WithDtor) + * stores a pointer to the passed variable, so the variable can be + * investigated after the struct isn't available anymore. + */ +struct WithDtor +{ + size_t* counter; + + this(ref size_t counter) @nogc nothrow pure @trusted + { + this.counter = &counter; + } + + ~this() @nogc nothrow pure @safe + { + if (this.counter !is null) + { + ++*this.counter; + } + } +} diff --git a/source/tanya/typecons.d b/source/tanya/typecons.d index 52fcbb2..becb1e3 100644 --- a/source/tanya/typecons.d +++ b/source/tanya/typecons.d @@ -22,6 +22,7 @@ import tanya.format; import tanya.functional; import tanya.meta.metafunction; import tanya.meta.trait; +version (unittest) import tanya.test.stub; /** * $(D_PSYMBOL Tuple) can store two or more heterogeneous objects. @@ -454,29 +455,24 @@ struct Option(T) // Moving @nogc nothrow pure @safe unittest { - static struct NotCopyable - { - @disable this(this); - } - - static assert(is(typeof(Option!NotCopyable(NotCopyable())))); + static assert(is(typeof(Option!NonCopyable(NonCopyable())))); // The value cannot be returned by reference because the default value // isn't passed by reference - static assert(!is(typeof(Option!DisabledPostblit().or(NotCopyable())))); + static assert(!is(typeof(Option!DisabledPostblit().or(NonCopyable())))); { - NotCopyable notCopyable; - static assert(is(typeof(Option!NotCopyable().or(notCopyable)))); + NonCopyable notCopyable; + static assert(is(typeof(Option!NonCopyable().or(notCopyable)))); } { - Option!NotCopyable option; + Option!NonCopyable option; assert(option.isNothing); - option = NotCopyable(); + option = NonCopyable(); assert(!option.isNothing); } { - Option!NotCopyable option; + Option!NonCopyable option; assert(option.isNothing); - option = Option!NotCopyable(NotCopyable()); + option = Option!NonCopyable(NonCopyable()); assert(!option.isNothing); } }