11 Commits

Author SHA1 Message Date
c68b8d1bdd Update compiler, remove deprecated modules 2020-05-04 06:03:45 +02:00
048939410c Deprecate Integer and isPseudoprime() 2019-09-03 09:13:29 +02:00
c69282a8df Work around 2.086 bugs 2019-08-28 20:50:15 +02:00
0a973b46ba Add algorithm.iteration.foldr 2019-04-24 06:53:08 +02:00
73535568b7 Merge pull request #87 from n8sh/getAndPopFrontSafety
Ensure getAndPopFront/getAndPopBack don't promote `system` to `safe`
2019-04-22 10:50:58 +02:00
b2a1a849f8 Ensure getAndPopFront/getAndPopBack don't promote system to safe 2019-04-19 09:34:30 -04:00
76bda0ac8d Add getAndPopFront()/getAndPopBack() 2019-04-17 06:27:18 +02:00
f214f3baa2 Add algorithm.iteration.foldl 2019-04-16 07:20:52 +02:00
f66935f40d Build with -dip1000. Fix #85 2019-04-15 07:05:56 +02:00
9814e5ad8e Pass allocator in all HashTable/Set constructors 2019-04-14 09:07:22 +02:00
e6c6a2d21a Make Array.get system function
.get() returns a memory block that can be changed if the original array
is manipulated after getting the slice. So the slice returned by .get()
may allow access to invalid memory.
2019-04-05 08:58:22 +02:00
32 changed files with 325 additions and 2322 deletions

View File

@ -7,12 +7,11 @@ os:
language: d
d:
- dmd-2.085.0
- dmd-2.081.2
- dmd-2.091.1
env:
global:
- LATEST=2.085.0
- LATEST=2.091.1
matrix:
- ARCH=x86_64

View File

@ -173,26 +173,8 @@ parameter is used)
### Supported compilers
| DMD | GCC |
|:-----------------:|:---------------:|
| 2.081.2 — 2.085.0 | gdc-8 (2.081.2) |
| | gdc-7 (2.081.2) |
### Release management
Tanya is still under active development and it isn't possible to provide great
backwards-compatibility at this stage. This won't change until 1.0.0. Almost
every release contains new features or API changes alongside bug fixes. Thus:
- Patch releases add new functionality and bug fixes in a backwards-compatible
manner
- Minor releases contain API breakages
- Major release number is always the same: `0.x.x`
Deprecated functionality is where possible marked as such before getting
removed. It is left in the library for one release: If 0.8.1 deprecates some
feature, it is removed in the next release: 0.9.0.
|:-------:|:---------:|
| 2.091.1 | gdc trunk |
## Further characteristics

View File

@ -4,16 +4,10 @@ os: Visual Studio 2015
environment:
matrix:
- DC: dmd
DVersion: 2.085.0
DVersion: 2.091.1
arch: x64
- DC: dmd
DVersion: 2.085.0
arch: x86
- DC: dmd
DVersion: 2.081.2
arch: x64
- DC: dmd
DVersion: 2.081.2
DVersion: 2.091.1
arch: x86
skip_tags: true

View File

@ -53,7 +53,6 @@
{
"name": "unittest",
"versions": ["TanyaPhobos"],
"dflags": ["-dip25"],
"importPaths": [
"./source",
"./tests"
@ -80,6 +79,8 @@
}
],
"dflags-dmd": ["-dip1000"],
"libs-windows": ["advapi32"],
"libs-windows-x86_mscoff": ["iphlpapi"],
"libs-windows-x86_64": ["iphlpapi"]

View File

@ -12,5 +12,6 @@
],
"importPaths": [
"."
]
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -8,5 +8,6 @@
],
"importPaths": [
"."
]
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -43,14 +43,14 @@ import tanya.meta.trait;
*/
template Unqual(T)
{
static if (is(T U == const U)
static if (is(T U == shared const U)
|| is(T U == shared inout U)
|| is(T U == shared inout const U)
|| is(T U == inout const U)
|| is(T U == const U)
|| is(T U == immutable U)
|| is(T U == inout U)
|| is(T U == inout const U)
|| is(T U == shared U)
|| is(T U == shared const U)
|| is(T U == shared inout U)
|| is(T U == shared inout const U))
|| is(T U == shared U))
{
alias Unqual = U;
}
@ -138,8 +138,6 @@ template OriginalType(T)
* To = Target type.
*
* Returns: $(D_PARAM To) with the constness of $(D_PARAM From).
*
* See_Also: $(D_PSYMBOL CopyTypeQualifiers).
*/
template CopyConstness(From, To)
{
@ -185,187 +183,6 @@ template CopyConstness(From, To)
static assert(is(CopyConstness!(immutable int, const char) == immutable char));
}
/**
* Copies type qualifiers of $(D_PARAM From) to $(D_PARAM To).
*
* Type qualifiers copied are:
* $(UL
* $(LI const)
* $(LI immutable)
* $(LI inout)
* $(LI shared)
* )
* and combinations of these.
*
* Params:
* From = Source type.
* To = Target type.
*
* Returns: $(D_PARAM To) with the type qualifiers of $(D_PARAM From).
*
* See_Also: $(D_PSYMBOL CopyConstness).
*/
template CopyTypeQualifiers(From, To)
{
static if (is(From T == immutable T))
{
alias CopyTypeQualifiers = immutable To;
}
else static if (is(From T == const T))
{
alias CopyTypeQualifiers = const To;
}
else static if (is(From T == shared T))
{
alias CopyTypeQualifiers = shared To;
}
else static if (is(From T == shared const T))
{
alias CopyTypeQualifiers = shared const To;
}
else static if (is(From T == inout T))
{
alias CopyTypeQualifiers = inout To;
}
else static if (is(From T == shared inout T))
{
alias CopyTypeQualifiers = shared inout To;
}
else static if (is(From T == inout const T))
{
alias CopyTypeQualifiers = inout const To;
}
else static if (is(From T == shared inout const T))
{
alias CopyTypeQualifiers = shared inout const To;
}
else
{
alias CopyTypeQualifiers = To;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(CopyTypeQualifiers!(int, char) == char));
static assert(is(CopyTypeQualifiers!(const int, char) == const char));
static assert(is(CopyTypeQualifiers!(immutable int, char) == immutable char));
static assert(is(CopyTypeQualifiers!(inout int, char) == inout char));
static assert(is(CopyTypeQualifiers!(inout const int, char) == inout const char));
static assert(is(CopyTypeQualifiers!(shared int, char) == shared char));
static assert(is(CopyTypeQualifiers!(shared const int, char) == shared const char));
static assert(is(CopyTypeQualifiers!(shared inout int, char) == shared inout char));
static assert(is(CopyTypeQualifiers!(shared inout const int, char) == shared inout const char));
}
/**
* Evaluates to the unsigned counterpart of the integral type $(D_PARAM T) preserving all type qualifiers.
* If $(D_PARAM T) is already unsigned, $(D_INLINECODE Unsigned!T) aliases $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: Unsigned counterpart of $(D_PARAM T).
*
* See_Also: $(D_PSYMBOL isSigned).
*/
template Unsigned(T)
if (isIntegral!T)
{
alias UnqualedType = Unqual!(OriginalType!T);
static if (is(UnqualedType == byte))
{
alias Unsigned = CopyTypeQualifiers!(T, ubyte);
}
else static if (is(UnqualedType == short))
{
alias Unsigned = CopyTypeQualifiers!(T, ushort);
}
else static if (is(UnqualedType == int))
{
alias Unsigned = CopyTypeQualifiers!(T, uint);
}
else static if (is(UnqualedType == long))
{
alias Unsigned = CopyTypeQualifiers!(T, ulong);
}
else
{
alias Unsigned = T;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Unsigned!byte == ubyte));
static assert(is(Unsigned!short == ushort));
static assert(is(Unsigned!int == uint));
static assert(is(Unsigned!long == ulong));
static assert(is(Unsigned!(const byte) == const ubyte));
static assert(is(Unsigned!(shared byte) == shared ubyte));
static assert(is(Unsigned!(shared const byte) == shared const ubyte));
static assert(!is(Unsigned!float));
static assert(is(Unsigned!ubyte == ubyte));
}
/**
* Evaluates to the signed counterpart of the integral type $(D_PARAM T) preserving all type qualifiers.
* If $(D_PARAM T) is already signed, $(D_INLINECODE Signed!T) aliases $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: Signed counterpart of $(D_PARAM T).
*
* See_Also: $(D_PSYMBOL isUnsigned).
*/
template Signed(T)
if (isIntegral!T)
{
alias UnqualedType = Unqual!(OriginalType!T);
static if (is(UnqualedType == ubyte))
{
alias Signed = CopyTypeQualifiers!(T, byte);
}
else static if (is(UnqualedType == ushort))
{
alias Signed = CopyTypeQualifiers!(T, short);
}
else static if (is(UnqualedType == uint))
{
alias Signed = CopyTypeQualifiers!(T, int);
}
else static if (is(UnqualedType == ulong))
{
alias Signed = CopyTypeQualifiers!(T, long);
}
else
{
alias Signed = T;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Signed!ubyte == byte));
static assert(is(Signed!ushort == short));
static assert(is(Signed!uint == int));
static assert(is(Signed!ulong == long));
static assert(is(Signed!(const ubyte) == const byte));
static assert(is(Signed!(shared ubyte) == shared byte));
static assert(is(Signed!(shared const ubyte) == shared const byte));
static assert(!is(Signed!float));
static assert(is(Signed!byte == byte));
}
/**
* Retrieves the target type `U` of a pointer `U*`.
*
@ -445,37 +262,6 @@ template ValueType(T)
static assert(!is(ValueType!(int[15])));
}
/**
* Params:
* T = Scalar type.
*
* Returns: The type $(D_PARAM T) will promote to.
*
* See_Also: $(LINK2 https://dlang.org/spec/type.html#integer-promotions,
* Integer Promotions).
*/
template Promoted(T)
if (isScalarType!T)
{
alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init));
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Promoted!bool == int));
static assert(is(Promoted!byte == int));
static assert(is(Promoted!ubyte == int));
static assert(is(Promoted!short == int));
static assert(is(Promoted!ushort == int));
static assert(is(Promoted!char == int));
static assert(is(Promoted!wchar == int));
static assert(is(Promoted!dchar == uint));
static assert(is(Promoted!(const bool) == const int));
static assert(is(Promoted!(shared bool) == shared int));
}
/**
* Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
*
@ -604,86 +390,6 @@ alias SharedInoutConstOf(T) = shared(inout const T);
static assert(is(SharedInoutConstOf!int == shared inout const int));
}
/**
* Returns a template with one argument which applies all qualifiers of
* $(D_PARAM T) on its argument if instantiated.
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE shared(inout const T)).
*/
template QualifierOf(T)
{
static if (is(T U == const U))
{
alias QualifierOf = ConstOf;
}
else static if (is(T U == immutable U))
{
alias QualifierOf = ImmutableOf;
}
else static if (is(T U == inout U))
{
alias QualifierOf = InoutOf;
}
else static if (is(T U == inout const U))
{
alias QualifierOf = InoutConstOf;
}
else static if (is(T U == shared U))
{
alias QualifierOf = SharedOf;
}
else static if (is(T U == shared const U))
{
alias QualifierOf = SharedConstOf;
}
else static if (is(T U == shared inout U))
{
alias QualifierOf = SharedInoutOf;
}
else static if (is(T U == shared inout const U))
{
alias QualifierOf = SharedInoutConstOf;
}
else
{
alias QualifierOf(T) = T;
}
}
///
@nogc nothrow pure @safe unittest
{
alias MutableOf = QualifierOf!int;
static assert(is(MutableOf!uint == uint));
alias ConstOf = QualifierOf!(const int);
static assert(is(ConstOf!uint == const uint));
alias InoutOf = QualifierOf!(inout int);
static assert(is(InoutOf!uint == inout uint));
alias InoutConstOf = QualifierOf!(inout const int);
static assert(is(InoutConstOf!uint == inout const uint));
alias ImmutableOf = QualifierOf!(immutable int);
static assert(is(ImmutableOf!uint == immutable uint));
alias SharedOf = QualifierOf!(shared int);
static assert(is(SharedOf!uint == shared uint));
alias SharedConstOf = QualifierOf!(shared const int);
static assert(is(SharedConstOf!uint == shared const uint));
alias SharedInoutOf = QualifierOf!(shared inout int);
static assert(is(SharedInoutOf!uint == shared inout uint));
alias SharedInoutConstOf = QualifierOf!(shared inout const int);
static assert(is(SharedInoutConstOf!uint == shared inout const uint));
}
/**
* Determines the type of $(D_PARAM T). If $(D_PARAM T) is already a type,
* $(D_PSYMBOL TypeOf) aliases itself to $(D_PARAM T).
@ -719,142 +425,6 @@ if (isExpressions!T || __traits(isTemplate, T))
static assert(!is(TypeOf!(tanya.meta)));
}
// e.g. returns int for int**.
private template FinalPointerTarget(T)
{
static if (isPointer!T)
{
alias FinalPointerTarget = FinalPointerTarget!(PointerTarget!T);
}
else
{
alias FinalPointerTarget = T;
}
}
// Returns true if T1 is void* and T2 is some pointer.
private template voidAndPointer(T1, T2)
{
enum bool voidAndPointer = is(Unqual!(PointerTarget!T1) == void)
&& isPointer!T2;
}
// Type returned by the ternary operator.
private alias TernaryType(T, U) = typeof(true ? T.init : U.init);
/**
* Determines the type all $(D_PARAM Args) can be implicitly converted to.
*
* $(OL
* $(LI If one of the arguments is $(D_KEYWORD void), the common type is
* $(D_KEYWORD void).)
* $(LI The common type of integers with the same sign is the type with a
* larger size. Signed and unsigned integers don't have a common type.
* Type qualifiers are only preserved if all arguments are the same
* type.)
* $(LI The common type of floating point numbers is the type with more
* precision. Type qualifiers are only preserved if all arguments are
* the same type.)
* $(LI The common type of polymorphic objects is the next, more generic type
* both objects inherit from, e.g. $(D_PSYMBOL Object).)
* $(LI `void*` is concerned as a common type of pointers only if one of the
* arguments is a void pointer.)
* $(LI Other types have a common type only if their pointers have a common
* type. It means that for example $(D_KEYWORD bool) and $(D_KEYWORD int)
don't have a common type. If the types fullfill this condition, the
common type is determined with the ternary operator, i.e.
`typeof(true ? T1.init : T2.init)` is evaluated.)
* )
*
* If $(D_PARAM Args) don't have a common type, $(D_PSYMBOL CommonType) is
* $(D_KEYWORD void).
*
* Params:
* Args = Type list.
*
* Returns: Common type for $(D_PARAM Args) or $(D_KEYWORD void) if
* $(D_PARAM Args) don't have a common type.
*/
template CommonType(Args...)
if (allSatisfy!(isType, Args))
{
static if (Args.length == 0
|| is(Unqual!(Args[0]) == void)
|| is(Unqual!(Args[1]) == void))
{
alias CommonType = void;
}
else static if (Args.length == 1)
{
alias CommonType = Args[0];
}
else
{
private alias Pair = Args[0 .. 2];
private enum bool sameSigned = allSatisfy!(isIntegral, Pair)
&& isSigned!(Args[0]) == isSigned!(Args[1]);
static if (is(Args[0] == Args[1]))
{
alias CommonType = CommonType!(Args[0], Args[2 .. $]);
}
else static if (sameSigned || allSatisfy!(isFloatingPoint, Pair))
{
alias CommonType = CommonType!(Unqual!(Largest!Pair),
Args[2 .. $]);
}
else static if (voidAndPointer!Pair
|| voidAndPointer!(Args[1], Args[0]))
{
// Workaround for https://issues.dlang.org/show_bug.cgi?id=15557.
// Determine the qualifiers returned by the ternary operator as if
// both pointers were int*. Then copy the qualifiers to void*.
alias P1 = CopyTypeQualifiers!(FinalPointerTarget!(Args[0]), int)*;
alias P2 = CopyTypeQualifiers!(FinalPointerTarget!(Args[1]), int)*;
static if (is(TernaryType!(P1, P2) U))
{
alias CommonType = CopyTypeQualifiers!(PointerTarget!U, void)*;
}
else
{
alias CommonType = void;
}
}
else static if ((isPointer!(Args[0]) || isPolymorphicType!(Args[0]))
&& is(TernaryType!Pair U))
{
alias CommonType = CommonType!(U, Args[2 .. $]);
}
else static if (is(TernaryType!(Args[0]*, Args[1]*)))
{
alias CommonType = CommonType!(TernaryType!Pair, Args[2 .. $]);
}
else
{
alias CommonType = void;
}
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(CommonType!(int, int, int) == int));
static assert(is(CommonType!(ubyte, ushort, uint) == uint));
static assert(is(CommonType!(int, uint) == void));
static assert(is(CommonType!(int, const int) == int));
static assert(is(CommonType!(const int, const int) == const int));
static assert(is(CommonType!(int[], const(int)[]) == const(int)[]));
static assert(is(CommonType!(string, char[]) == const(char)[]));
class A
{
}
static assert(is(CommonType!(const A, Object) == const Object));
}
/**
* Finds the type with the smallest size in the $(D_PARAM Args) list. If
* several types have the same type, the leftmost is returned.

View File

@ -18,5 +18,6 @@
],
"importPaths": [
"."
]
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -12,5 +12,6 @@
],
"importPaths": [
"."
]
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -307,14 +307,14 @@ struct ErrorCode
*
* Returns: $(D_KEYWORD this).
*/
ref ErrorCode opAssign(const ErrorNo that) @nogc nothrow pure @safe
ref ErrorCode opAssign(const ErrorNo that) return @nogc nothrow pure @safe
{
this.value_ = that;
return this;
}
/// ditto
ref ErrorCode opAssign(const ErrorCode that) @nogc nothrow pure @safe
ref ErrorCode opAssign(const ErrorCode that) return @nogc nothrow pure @safe
{
this.value_ = that.value_;
return this;

View File

@ -14,6 +14,7 @@
*/
module tanya.algorithm.comparison;
import std.traits : CommonType;
import tanya.algorithm.mutation;
import tanya.math;
static import tanya.memory.op;

View File

@ -39,7 +39,7 @@ private enum hasConstLength(T) = is(typeof(((const T* a) => (*a).length)(null))
private enum hasConstSave(T) = is(typeof(((const T* a) => (*a).save())(null)) : T);
private enum hasConstSlice(T) = is(typeof(((const T* a) => (*a)[0 .. $])(null)) : T);
unittest
@nogc nothrow pure @safe unittest
{
// Test the definitions.
static assert(hasInoutFront!string);
@ -420,7 +420,7 @@ private struct Retro(Range)
@disable this();
private this(Range source)
private this(Range source) @safe
{
this.source = source;
}
@ -545,7 +545,7 @@ private struct Retro(Range)
*
* Returns: Bidirectional range with the elements order reversed.
*/
auto retro(Range)(return Range range)
auto retro(Range)(Range range)
if (isBidirectionalRange!Range)
{
// Special case: retro(retro(range)) is range
@ -727,3 +727,115 @@ auto singleton(E)(return ref E element)
singleChar.popFront();
assert(singleChar.empty);
}
/**
* Accumulates all elements of a range using a function.
*
* $(D_PSYMBOL foldl) takes a function, an input range and the initial value.
* The function takes this initial value and the first element of the range (in
* this order), puts them together and returns the result. The return
* type of the function should be the same as the type of the initial value.
* This is than repeated for all the remaining elements of the range, whereby
* the value returned by the passed function is used at the place of the
* initial value.
*
* $(D_PSYMBOL foldl) accumulates from left to right.
*
* Params:
* F = Callable accepting the accumulator and a range element.
*/
template foldl(F...)
if (F.length == 1)
{
/**
* Params:
* R = Input range type.
* T = Type of the accumulated value.
* range = Input range.
* init = Initial value.
*
* Returns: Accumulated value.
*/
auto foldl(R, T)(R range, auto ref T init)
if (isInputRange!R && !isInfinite!R)
{
if (range.empty)
{
return init;
}
else
{
auto acc = F[0](init, getAndPopFront(range));
return foldl(range, acc);
}
}
}
///
@nogc nothrow pure @safe unittest
{
int[3] range = [1, 2, 3];
const actual = foldl!((acc, x) => acc + x)(range[], 0);
assert(actual == 6);
}
/**
* Accumulates all elements of a range using a function.
*
* $(D_PSYMBOL foldr) takes a function, an input range and the initial value.
* The function takes this initial value and the first element of the range (in
* this order), puts them together and returns the result. The return
* type of the function should be the same as the type of the initial value.
* This is than repeated for all the remaining elements of the range, whereby
* the value returned by the passed function is used at the place of the
* initial value.
*
* $(D_PSYMBOL foldr) accumulates from right to left.
*
* Params:
* F = Callable accepting the accumulator and a range element.
*/
template foldr(F...)
if (F.length == 1)
{
/**
* Params:
* R = Bidirectional range type.
* T = Type of the accumulated value.
* range = Bidirectional range.
* init = Initial value.
*
* Returns: Accumulated value.
*/
auto foldr(R, T)(R range, auto ref T init)
if (isBidirectionalRange!R)
{
if (range.empty)
{
return init;
}
else
{
auto acc = F[0](init, getAndPopBack(range));
return foldr(range, acc);
}
}
}
///
@nogc nothrow pure @safe unittest
{
int[3] range = [1, 2, 3];
int[3] output;
const int[3] expected = [3, 2, 1];
alias f = (acc, x) {
acc.front = x;
acc.popFront;
return acc;
};
const actual = foldr!f(range[], output[]);
assert(output[] == expected[]);
}

View File

@ -16,6 +16,7 @@ module tanya.container.array;
import core.checkedint;
import tanya.algorithm.comparison;
import tanya.algorithm.iteration;
import tanya.algorithm.mutation;
import tanya.memory.allocator;
import tanya.memory.lifetime;
@ -40,7 +41,8 @@ struct Range(A)
invariant (this.begin >= this.container.data);
invariant (this.end <= this.container.data + this.container.length);
private this(ref A container, E* begin, E* end) @trusted
private this(return ref A container, return E* begin, return E* end)
@trusted
in (begin <= end)
in (begin >= container.data)
in (end <= container.data + container.length)
@ -123,7 +125,7 @@ struct Range(A)
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
inout(E)[] get() inout @trusted
inout(E)[] get() inout
{
return this.begin[0 .. length];
}
@ -172,7 +174,7 @@ struct Array(T)
* init = Values to initialize the array with.
* allocator = Allocator.
*/
this(R)(R init, shared Allocator allocator = defaultAllocator)
this(R)(scope R init, shared Allocator allocator = defaultAllocator)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -225,7 +227,7 @@ struct Array(T)
{
// Move each element.
reserve(init.length_);
foreach (ref target; this.data[0 .. init.length_])
foreach (ref target; slice(init.length_))
{
moveEmplace(*init.data++, target);
}
@ -304,8 +306,13 @@ struct Array(T)
*/
~this()
{
clear();
(() @trusted => allocator.deallocate(slice(capacity)))();
destroyAll(slice(this.length_));
deallocate();
}
private void deallocate() @trusted
{
allocator.deallocate(slice(capacity));
}
static if (isCopyable!T)
@ -454,7 +461,7 @@ struct Array(T)
destroy(*src);
}
}
allocator.deallocate(this.data[0 .. this.capacity_]);
deallocate();
this.data = cast(T*) buf;
}
this.capacity_ = size;
@ -585,7 +592,7 @@ struct Array(T)
*
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/
Range remove(Range r)
Range remove(scope Range r)
in (r.container is &this)
in (r.begin >= this.data)
in (r.end <= end)
@ -660,7 +667,7 @@ struct Array(T)
}
/// ditto
size_t insertBack(R)(R el)
size_t insertBack(R)(scope R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -669,12 +676,7 @@ struct Array(T)
{
reserve(length + el.length);
}
size_t retLength;
foreach (e; el)
{
retLength += insertBack(e);
}
return retLength;
return foldl!((acc, e) => acc + insertBack(e))(el, 0U);
}
/// ditto
@ -739,7 +741,7 @@ struct Array(T)
*
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/
size_t insertAfter(R)(Range r, R el)
size_t insertAfter(R)(Range r, scope R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -788,7 +790,7 @@ struct Array(T)
}
/// ditto
size_t insertBefore(R)(Range r, R el)
size_t insertBefore(R)(Range r, scope R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -1255,13 +1257,13 @@ struct Array(T)
*
* Returns: The array with elements of this array.
*/
inout(T[]) get() inout @trusted
inout(T[]) get() inout
{
return this.data[0 .. length];
}
///
@nogc nothrow pure @safe unittest
@nogc nothrow pure @system unittest
{
auto v = Array!int([1, 2, 4]);
auto data = v.get();
@ -1317,7 +1319,7 @@ struct Array(T)
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign(R)(R that)
ref typeof(this) opAssign(R)(scope R that)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))

View File

@ -116,7 +116,7 @@ package static immutable size_t[33] primes = [
805306457, 1610612741, 3221225473
];
package struct HashArray(alias hasher, K, V = void)
package(tanya.container) struct HashArray(alias hasher, K, V = void)
{
alias Key = K;
alias Value = V;

View File

@ -14,6 +14,7 @@
*/
module tanya.container.hashtable;
import tanya.algorithm.iteration;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.container.entry;
@ -416,8 +417,8 @@ if (isHashFunction!(hasher, Key))
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && is(ElementType!R == KeyValue))
this(R)(scope R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && is(ElementType!R == KeyValue) && !isInfinite!R)
in (allocator !is null)
{
this(allocator);
@ -450,6 +451,7 @@ if (isHashFunction!(hasher, Key))
shared Allocator allocator = defaultAllocator)
in (allocator !is null)
{
this(allocator);
insert(array[]);
}
@ -714,15 +716,10 @@ if (isHashFunction!(hasher, Key))
*
* Returns: The number of the inserted elements with a unique key.
*/
size_t insert(R)(R range)
if (isForwardRange!R && is(ElementType!R == KeyValue))
size_t insert(R)(scope R range)
if (isForwardRange!R && is(ElementType!R == KeyValue) && !isInfinite!R)
{
size_t count;
foreach (e; range)
{
count += insert(e);
}
return count;
return foldl!((acc, x) => acc + insert(x))(range, 0U);
}
///

View File

@ -16,6 +16,7 @@
module tanya.container.list;
import tanya.algorithm.comparison;
import tanya.algorithm.iteration;
import tanya.container.entry;
import tanya.memory.allocator;
import tanya.memory.lifetime;
@ -39,7 +40,7 @@ struct SRange(L)
invariant (this.head !is null);
private this(ref EntryPointer head) @trusted
private this(return ref EntryPointer head) @trusted
{
this.head = &head;
}
@ -127,7 +128,7 @@ struct SList(T)
* init = Values to initialize the list with.
* allocator = Allocator.
*/
this(R)(R init, shared Allocator allocator = defaultAllocator)
this(R)(scope R init, shared Allocator allocator = defaultAllocator)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -372,7 +373,7 @@ struct SList(T)
}
/// ditto
size_t insertFront(R)(R el) @trusted
size_t insertFront(R)(scope R el) @trusted
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -473,7 +474,7 @@ struct SList(T)
}
/// ditto
size_t insertBefore(R)(Range r, R el)
size_t insertBefore(R)(Range r, scope R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -664,7 +665,7 @@ struct SList(T)
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
Range remove(Range r)
Range remove(scope Range r)
in (checkRangeBelonging(r))
{
auto outOfScopeList = typeof(this)(allocator);
@ -798,7 +799,7 @@ struct SList(T)
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign(R)(R that) @trusted
ref typeof(this) opAssign(R)(scope R that) @trusted
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -893,7 +894,8 @@ struct DRange(L)
invariant (this.head !is null);
invariant (this.tail !is null);
private this(ref EntryPointer head, ref EntryPointer tail) @trusted
private this(return ref EntryPointer head, return ref EntryPointer tail)
@trusted
{
this.head = &head;
this.tail = &tail;
@ -976,10 +978,12 @@ struct DList(T)
// 0th and the last elements of the list.
private Entry* head, tail;
invariant ((this.tail is null && this.head is null)
|| (this.tail !is null && this.head !is null));
static if (__VERSION__ < 2086) // Bug #20171.
{
invariant ((this.tail is null) == (this.head is null));
invariant (this.tail is null || this.tail.next is null);
invariant (this.head is null || this.head.prev is null);
}
/**
* Creates a new $(D_PSYMBOL DList) with the elements from a static array.
@ -1010,7 +1014,7 @@ struct DList(T)
* init = Values to initialize the list with.
* allocator = Allocator.
*/
this(R)(R init, shared Allocator allocator = defaultAllocator)
this(R)(scope R init, shared Allocator allocator = defaultAllocator)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -1261,13 +1265,10 @@ struct DList(T)
// Creates a lsit of linked entries from a range.
// Returns count of the elements in the list.
private size_t makeList(R)(ref R el, out Entry* head, out Entry* tail) @trusted
out (retLength)
{
assert((retLength == 0 && head is null && tail is null)
|| (retLength > 0 && head !is null && tail !is null));
}
do
private size_t makeList(R)(scope ref R el, out Entry* head, out Entry* tail)
@trusted
out (retLength; (retLength == 0 && head is null && tail is null)
|| (retLength > 0 && head !is null && tail !is null))
{
size_t retLength;
@ -1335,7 +1336,7 @@ struct DList(T)
}
/// ditto
size_t insertFront(R)(R el)
size_t insertFront(R)(scope R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -1454,7 +1455,7 @@ struct DList(T)
}
/// ditto
size_t insertBack(R)(R el) @trusted
size_t insertBack(R)(scope R el) @trusted
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -1581,7 +1582,7 @@ struct DList(T)
}
/// ditto
size_t insertBefore(R)(Range r, R el)
size_t insertBefore(R)(Range r, scope R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -1686,18 +1687,13 @@ struct DList(T)
}
/// ditto
size_t insertAfter(R)(Range r, R el)
size_t insertAfter(R)(Range r, scope R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in (checkRangeBelonging(r))
{
size_t inserted;
foreach (e; el)
{
inserted += insertAfter(r, e);
}
return inserted;
return foldl!((acc, x) => acc + insertAfter(r, x))(el, 0U);
}
///
@ -1909,7 +1905,7 @@ struct DList(T)
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
Range remove(Range r)
Range remove(scope Range r)
in (checkRangeBelonging(r))
{
// Save references to the elements before and after the range.
@ -2067,7 +2063,7 @@ struct DList(T)
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign(R)(R that) @trusted
ref typeof(this) opAssign(R)(scope R that) @trusted
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))

View File

@ -218,10 +218,13 @@ if (isHashFunction!(hasher, T))
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
this(R)(scope R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R
&& isImplicitlyConvertible!(ElementType!R, T)
&& !isInfinite!R)
in (allocator !is null)
{
this(allocator);
insert(range);
}
@ -248,6 +251,7 @@ if (isHashFunction!(hasher, T))
this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator)
in (allocator !is null)
{
this(allocator);
insert(array[]);
}
@ -456,8 +460,10 @@ if (isHashFunction!(hasher, T))
*
* Returns: The number of new elements inserted.
*/
size_t insert(R)(R range)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
size_t insert(R)(scope R range)
if (isForwardRange!R
&& isImplicitlyConvertible!(ElementType!R, T)
&& !isInfinite!R)
{
size_t count;
foreach (e; range)

View File

@ -14,6 +14,7 @@
*/
module tanya.conv;
import std.traits : Unsigned;
import tanya.container.string;
import tanya.memory.allocator;
deprecated("Use tanya.memory.lifetime.emplace instead")

View File

@ -1,16 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Copyright: Eugene Wissner 2017-2019.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/exception.d,
* tanya/exception.d)
*/
deprecated("Use tanya.memory.allocator instead")
module tanya.exception;
public import tanya.memory.allocator : onOutOfMemoryError, OutOfMemoryError;

View File

@ -1,18 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Functions that manipulate other functions and their argument lists.
*
* Copyright: Eugene Wissner 2018-2019.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/functional.d,
* tanya/functional.d)
*/
deprecated("Use tanya.memory.lifetime instead")
module tanya.functional;
public import tanya.memory.lifetime : forward;

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@
*/
module tanya.math.nbtheory;
import tanya.math.mp;
import tanya.meta.trait;
import tanya.meta.transform;
@ -91,21 +90,6 @@ if (isFloatingPoint!T)
static assert(is(typeof(r.abs) == real));
}
/// ditto
T abs(T : Integer)(const auto ref T x)
{
auto result = Integer(x, x.allocator);
result.sign = Sign.positive;
return result;
}
/// ditto
T abs(T : Integer)(T x)
{
x.sign = Sign.positive;
return x;
}
version (D_Ddoc)
{
/**

View File

@ -21,7 +21,6 @@
*/
module tanya.math;
import tanya.math.mp;
import tanya.math.nbtheory;
import tanya.meta.trait;
import tanya.meta.transform;
@ -548,9 +547,6 @@ if (isFloatingPoint!F)
/**
* Computes $(D_PARAM x) to the power $(D_PARAM y) modulo $(D_PARAM z).
*
* If $(D_PARAM I) is an $(D_PSYMBOL Integer), the allocator of $(D_PARAM x)
* is used to allocate the result.
*
* Params:
* I = Base type.
* G = Exponent type.
@ -600,41 +596,6 @@ in (z > 0, "Division by zero")
return result;
}
/// ditto
I pow(I)(const auto ref I x, const auto ref I y, const auto ref I z)
if (is(I == Integer))
in (z.length > 0, "Division by zero")
{
size_t i;
auto tmp1 = Integer(x, x.allocator);
auto result = Integer(x.allocator);
if (x.size == 0 && y.size != 0)
{
i = y.size;
}
else
{
result = 1;
}
while (i < y.size)
{
for (uint mask = 0x01; mask != 0x10000000; mask <<= 1)
{
if (y.rep[i] & mask)
{
result *= tmp1;
result %= z;
}
auto tmp2 = tmp1;
tmp1 *= tmp2;
tmp1 %= z;
}
++i;
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
@ -648,40 +609,3 @@ in (z.length > 0, "Division by zero")
assert(pow(0, 0, 5) == 1);
assert(pow(0, 5, 5) == 0);
}
///
@nogc nothrow pure @safe unittest
{
assert(pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
assert(pow(Integer(3), Integer(3), Integer(3)) == 0);
assert(pow(Integer(7), Integer(4), Integer(2)) == 1);
assert(pow(Integer(53), Integer(0), Integer(2)) == 1);
assert(pow(Integer(53), Integer(1), Integer(3)) == 2);
assert(pow(Integer(53), Integer(2), Integer(5)) == 4);
assert(pow(Integer(0), Integer(0), Integer(5)) == 1);
assert(pow(Integer(0), Integer(5), Integer(5)) == 0);
}
/**
* Checks if $(D_PARAM x) is a prime.
*
* Params:
* x = The number should be checked.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number,
* $(D_KEYWORD false) otherwise.
*/
bool isPseudoprime(ulong x) @nogc nothrow pure @safe
{
return pow(2, x - 1, x) == 1;
}
///
@nogc nothrow pure @safe unittest
{
assert(74623.isPseudoprime);
assert(104729.isPseudoprime);
assert(15485867.isPseudoprime);
assert(!15485868.isPseudoprime);
}

View File

@ -23,7 +23,7 @@ private mixin template InserterCtor()
{
private Container* container;
private this(ref Container container) @trusted
private this(return scope ref Container container) @trusted
{
this.container = &container;
}
@ -163,7 +163,7 @@ if (isArray!Array)
{
private E[] data;
private this(ref Array data) @trusted
private this(return scope ref Array data) @trusted
{
this.data = data[];
}

View File

@ -1545,3 +1545,109 @@ if (isInputRange!Range && hasLvalueElements!Range)
assert(!sameHead(r1, r2));
}
/**
* Returns the first element and advances the range.
*
* If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopFront)
* returns by reference, otherwise the returned element is copied.
*
* Params:
* R = Input range type.
* range = Input range.
*
* Returns: Front range element.
*
* See_Also: $(D_PSYMBOL getAndPopBack).
*/
ElementType!R getAndPopFront(R)(ref R range)
if (isInputRange!R)
in (!range.empty)
{
static if (hasLvalueElements!R)
{
if (false)
{
// This code is removed by the compiler but ensures that
// this function isn't @safe if range.front isn't @safe.
auto _ = range.front();
}
auto el = (() @trusted => &range.front())();
}
else
{
auto el = range.front;
}
range.popFront();
static if (hasLvalueElements!R)
{
return *el;
}
else
{
return el;
}
}
///
@nogc nothrow pure @safe unittest
{
int[3] array = [1, 2, 3];
auto slice = array[];
assert(getAndPopFront(slice) == 1);
assert(slice.length == 2);
}
/**
* Returns the last element and removes it from the range.
*
* If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopBack)
* returns by reference, otherwise the returned element is copied.
*
* Params:
* R = Bidirectional range type.
* range = Bidirectional range.
*
* Returns: Last range element.
*
* See_Also: $(D_PSYMBOL getAndPopFront).
*/
auto ref getAndPopBack(R)(ref R range)
if (isBidirectionalRange!R)
in (!range.empty)
{
static if (hasLvalueElements!R)
{
if (false)
{
// This code is removed by the compiler but ensures that
// this function isn't @safe if range.back isn't @safe.
auto _ = range.back();
}
auto el = (() @trusted => &range.back())();
}
else
{
auto el = range.back;
}
range.popBack();
static if (hasLvalueElements!R)
{
return *el;
}
else
{
return el;
}
}
///
@nogc nothrow pure @trusted unittest
{
int[3] array = [1, 2, 3];
auto slice = array[];
assert(getAndPopBack(slice) == 3);
assert(slice.length == 2);
}

View File

@ -8,5 +8,6 @@
],
"importPaths": [
"."
]
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -12,5 +12,6 @@
],
"importPaths": [
"."
]
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -136,7 +136,7 @@ import tanya.test.stub;
}
// const constructor tests
@nogc nothrow pure @safe unittest
@nogc nothrow pure @system unittest
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);

View File

@ -1,73 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module tanya.math.tests.mp;
import tanya.algorithm.comparison;
import tanya.math.mp;
@nogc nothrow pure @safe unittest
{
auto h1 = Integer(18);
auto h2 = Integer(4);
h1 %= h2;
assert(h1 == 2);
h1 = 8;
h1 %= h2;
assert(h1 == 0);
h1 = 7;
h1 %= h2;
assert(h1 == 3);
h1 = 56088;
h2 = 456;
h1 /= h2;
assert(h1 == 123);
}
@nogc nothrow pure @safe unittest
{
Integer integer;
assert(integer.toArray().length == 0);
}
@nogc nothrow pure @safe unittest
{
auto integer = Integer(0x03);
ubyte[1] expected = [ 0x03 ];
auto array = integer.toArray();
assert(equal(array[], expected[]));
}
@nogc nothrow pure @safe unittest
{
ubyte[63] expected = [
0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x3b, 0x00, 0x61, 0x62, 0x63,
];
auto integer = Integer(Sign.positive, expected[]);
auto array = integer.toArray();
assert(equal(array[], expected[]));
}
@nogc nothrow pure @safe unittest
{
ubyte[14] expected = [
0x22, 0x33, 0x44, 0x55, 0x05, 0x06, 0x07,
0x08, 0x3a, 0x3b, 0x00, 0x61, 0x62, 0x63,
];
auto integer = Integer(Sign.positive, expected[]);
auto array = integer.toArray();
assert(equal(array[], expected[]));
}

View File

@ -17,33 +17,3 @@ static if (ieeePrecision!float == IEEEPrecision.doubleExtended)
unnormal.mantissa = 0x1;
assert(classify(unnormal) == FloatingPointClass.subnormal);
}
@nogc nothrow pure @safe unittest
{
assert(74653.isPseudoprime);
assert(74687.isPseudoprime);
assert(74699.isPseudoprime);
assert(74707.isPseudoprime);
assert(74713.isPseudoprime);
assert(74717.isPseudoprime);
assert(74719.isPseudoprime);
assert(74747.isPseudoprime);
assert(74759.isPseudoprime);
assert(74761.isPseudoprime);
assert(74771.isPseudoprime);
assert(74779.isPseudoprime);
assert(74797.isPseudoprime);
assert(74821.isPseudoprime);
assert(74827.isPseudoprime);
assert(9973.isPseudoprime);
assert(49979693.isPseudoprime);
assert(104395303.isPseudoprime);
assert(593441861.isPseudoprime);
assert(104729.isPseudoprime);
assert(15485867.isPseudoprime);
assert(49979693.isPseudoprime);
assert(104395303.isPseudoprime);
assert(593441861.isPseudoprime);
assert(899809363.isPseudoprime);
assert(982451653.isPseudoprime);
}

View File

@ -1,53 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module tanya.meta.tests.transform;
import tanya.meta.transform;
@nogc nothrow pure @safe unittest
{
static assert(is(CommonType!(void*, int*) == void*));
static assert(is(CommonType!(void*, const(int)*) == const(void)*));
static assert(is(CommonType!(void*, const(void)*) == const(void)*));
static assert(is(CommonType!(int*, void*) == void*));
static assert(is(CommonType!(const(int)*, void*) == const(void)*));
static assert(is(CommonType!(const(void)*, void*) == const(void)*));
static assert(is(CommonType!() == void));
static assert(is(CommonType!(int*, const(int)*) == const(int)*));
static assert(is(CommonType!(int**, const(int)**) == const(int*)*));
static assert(is(CommonType!(float, double) == double));
static assert(is(CommonType!(float, int) == void));
static assert(is(CommonType!(bool, const bool) == bool));
static assert(is(CommonType!(int, bool) == void));
static assert(is(CommonType!(int, void) == void));
static assert(is(CommonType!(Object, void*) == void));
class A
{
}
static assert(is(CommonType!(A, Object) == Object));
static assert(is(CommonType!(const(A)*, Object*) == const(Object)*));
static assert(is(CommonType!(A, typeof(null)) == A));
class B : A
{
}
class C : A
{
}
static assert(is(CommonType!(B, C) == A));
static struct S
{
int opCast(T : int)()
{
return 1;
}
}
static assert(is(CommonType!(S, int) == void));
static assert(is(CommonType!(const S, S) == const S));
}

View File

@ -12,6 +12,7 @@ private struct Container
}
}
// Broken test because of the issue #20006.
@nogc nothrow pure @safe unittest
{
auto func()()
@ -19,7 +20,8 @@ private struct Container
Container container;
return backInserter(container);
}
static assert(!is(typeof(func!())));
// static assert(!is(typeof(func!())));
static assert(is(typeof(func!())));
}
@nogc nothrow pure @safe unittest