aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2025-01-08 21:02:56 +0100
committerIain Buclaw <ibuclaw@gdcproject.org>2025-01-12 21:53:50 +0100
commit0dd21bce3afe9b0081d1d5bf4b53f9b43980c1c4 (patch)
tree4326f2031f50a7a326d79c25589ff50d00e36e77 /libphobos/src/std
parenta236f70617213343f3075ee43e8d9f5882dca400 (diff)
downloadgcc-0dd21bce3afe9b0081d1d5bf4b53f9b43980c1c4.zip
gcc-0dd21bce3afe9b0081d1d5bf4b53f9b43980c1c4.tar.gz
gcc-0dd21bce3afe9b0081d1d5bf4b53f9b43980c1c4.tar.bz2
d: Merge upstream dmd, druntime c57da0cf59, phobos ad8ee5587
D front-end changes: - Import latest fixes from dmd v2.110.0-beta.1. - The `align' attribute now allows to specify `default' explicitly. - Add primary expression of the form `__rvalue(expression)' which causes `expression' to be treated as an rvalue, even if it is an lvalue. - Shortened method syntax can now be used in constructors. D runtime changes: - Import latest fixes from druntime v2.110.0-beta.1. Phobos changes: - Import latest fixes from phobos v2.110.0-beta.1. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd c57da0cf59. * d-codegen.cc (can_elide_copy_p): New. (d_build_call): Use it. * d-lang.cc (d_post_options): Update for new front-end interface. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime c57da0cf59. * src/MERGE: Merge upstream phobos ad8ee5587. * testsuite/libphobos.init_fini/custom_gc.d: Adjust test. gcc/testsuite/ChangeLog: * gdc.dg/copy1.d: New test.
Diffstat (limited to 'libphobos/src/std')
-rw-r--r--libphobos/src/std/algorithm/searching.d101
-rw-r--r--libphobos/src/std/array.d58
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d621
-rw-r--r--libphobos/src/std/format/package.d4
-rw-r--r--libphobos/src/std/functional.d294
-rw-r--r--libphobos/src/std/logger/filelogger.d2
-rw-r--r--libphobos/src/std/logger/package.d4
-rw-r--r--libphobos/src/std/math/traits.d52
-rw-r--r--libphobos/src/std/path.d2
-rw-r--r--libphobos/src/std/process.d6
-rw-r--r--libphobos/src/std/stdio.d47
11 files changed, 965 insertions, 226 deletions
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
index b7119d2..e0d2435 100644
--- a/libphobos/src/std/algorithm/searching.d
+++ b/libphobos/src/std/algorithm/searching.d
@@ -792,19 +792,18 @@ if (isInputRange!R && !isInfinite!R)
`haystack` before reaching an element for which
`startsWith!pred(haystack, needles)` is `true`. If
`startsWith!pred(haystack, needles)` is not `true` for any element in
- `haystack`, then `-1` is returned. If only `pred` is provided,
- `pred(haystack)` is tested for each element.
+ `haystack`, then `-1` is returned. If more than one needle is provided,
+ `countUntil` will wrap the result in a tuple similar to
+ `Tuple!(ptrdiff_t, "steps", ptrdiff_t needle)`
See_Also: $(REF indexOf, std,string)
+/
-ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
+auto countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
if (isForwardRange!R
&& Rs.length > 0
&& isForwardRange!(Rs[0]) == isInputRange!(Rs[0])
&& allSatisfy!(canTestStartsWith!(pred, R), Rs))
{
- typeof(return) result;
-
static if (needles.length == 1)
{
static if (hasLength!R) //Note: Narrow strings don't have length.
@@ -814,14 +813,16 @@ if (isForwardRange!R
auto len = haystack.length;
auto r2 = find!pred(haystack, needles[0]);
if (!r2.empty)
- return cast(typeof(return)) (len - r2.length);
+ return ptrdiff_t(len - r2.length);
}
else
{
import std.range : dropOne;
if (needles[0].empty)
- return 0;
+ return ptrdiff_t(0);
+
+ ptrdiff_t result;
//Default case, slower route doing startsWith iteration
for ( ; !haystack.empty ; ++result )
@@ -836,21 +837,39 @@ if (isForwardRange!R
//haystack we pop in all paths, so we do that, and then save.
haystack.popFront();
if (startsWith!pred(haystack.save, needles[0].save.dropOne()))
- return result;
+ return result;
}
else
- haystack.popFront();
+ {
+ haystack.popFront();
+ }
}
}
+ return ptrdiff_t(-1);
}
else
{
+ static struct Result
+ {
+ ptrdiff_t steps, needle; // both -1 when nothing was found
+ alias steps this; // compatible with previous ptrdiff_t return type
+ ptrdiff_t opIndex(size_t idx) // faking a tuple
+ {
+ assert(idx < 2, "Type has only two members: pos and needle");
+ return idx == 0 ? steps : needle;
+ }
+ }
+ Result result;
+
foreach (i, Ri; Rs)
{
static if (isForwardRange!Ri)
{
if (needles[i].empty)
- return 0;
+ {
+ result.needle = i;
+ return result;
+ }
}
}
Tuple!Rs t;
@@ -861,7 +880,7 @@ if (isForwardRange!R
t[i] = needles[i];
}
}
- for (; !haystack.empty ; ++result, haystack.popFront())
+ for (; !haystack.empty ; ++result.steps, haystack.popFront())
{
foreach (i, Ri; Rs)
{
@@ -870,18 +889,18 @@ if (isForwardRange!R
t[i] = needles[i].save;
}
}
- if (startsWith!pred(haystack.save, t.expand))
+ if (auto needle = startsWith!pred(haystack.save, t.expand))
{
+ result.needle = needle - 1;
return result;
}
}
- }
- // Because of https://issues.dlang.org/show_bug.cgi?id=8804
- // Avoids both "unreachable code" or "no return statement"
- static if (isInfinite!R) assert(false, R.stringof ~ " must not be an"
- ~ " infinite range");
- else return -1;
+ // no match was found
+ result.needle = -1;
+ result.steps = -1;
+ return result;
+ }
}
/// ditto
@@ -906,6 +925,16 @@ if (isInputRange!R &&
assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2);
assert(countUntil([0, 7, 12, 22, 9], 9) == 4);
assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3);
+
+ // supports multiple needles
+ auto res = "...hello".countUntil("ha", "he");
+ assert(res.steps == 3);
+ assert(res.needle == 1);
+
+ // returns -1 if no needle was found
+ res = "hello".countUntil("ha", "hu");
+ assert(res.steps == -1);
+ assert(res.needle == -1);
}
@safe unittest
@@ -943,6 +972,42 @@ if (isInputRange!R &&
assert(countUntil("hello world", "world", 'l') == 2);
}
+@safe unittest
+{
+ auto res = "...hello".countUntil("hella", "hello");
+ assert(res == 3);
+ assert(res.steps == 3);
+ assert(res.needle == 1);
+ // test tuple emulation
+ assert(res[0] == 3);
+ assert(res[1] == 1);
+
+ // the first matching needle is chosen
+ res = "...hello".countUntil("hell", "hello");
+ assert(res == 3);
+ assert(res.needle == 0);
+
+ // no match
+ auto noMatch = "hello world".countUntil("ha", "hu");
+ assert(noMatch == -1);
+ assert(noMatch.steps == -1);
+ assert(noMatch.needle == -1);
+ // test tuple emulation
+ assert(noMatch[0] == -1);
+ assert(noMatch[1] == -1);
+}
+
+// test infinite ranges
+@safe unittest
+{
+ import std.algorithm.iteration : joiner;
+ import std.range : iota, repeat;
+ import std.stdio;
+ assert(10.iota.repeat.joiner.countUntil(9) == 9);
+ assert(10.iota.repeat.joiner.countUntil(1, 2) == 1);
+ assert(10.iota.repeat.joiner.countUntil!(a => a >= 9) == 9);
+}
+
/// ditto
ptrdiff_t countUntil(alias pred, R)(R haystack)
if (isInputRange!R &&
diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d
index 3313dbb..fea7025 100644
--- a/libphobos/src/std/array.d
+++ b/libphobos/src/std/array.d
@@ -1297,6 +1297,64 @@ if (is(typeof(a.ptr < b.ptr) == bool))
static assert(test == "three"d);
}
+///
+@safe pure nothrow unittest
+{
+ import std.meta : AliasSeq;
+
+ // can be used as an alternative implementation of overlap that returns
+ // `true` or `false` instead of a slice of the overlap
+ bool isSliceOf(T)(const scope T[] part, const scope T[] whole)
+ {
+ return part.overlap(whole) is part;
+ }
+
+ auto x = [1, 2, 3, 4, 5];
+
+ assert(isSliceOf(x[3..$], x));
+ assert(isSliceOf(x[], x));
+ assert(!isSliceOf(x, x[3..$]));
+ assert(!isSliceOf([7, 8], x));
+ assert(isSliceOf(null, x));
+
+ // null is a slice of itself
+ assert(isSliceOf(null, null));
+
+ foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
+ {
+ T a = [1, 2, 3, 4, 5];
+ T b = a;
+ T c = a[1 .. $];
+ T d = a[0 .. 1];
+ T e = null;
+
+ assert(isSliceOf(a, a));
+ assert(isSliceOf(b, a));
+ assert(isSliceOf(a, b));
+
+ assert(isSliceOf(c, a));
+ assert(isSliceOf(c, b));
+ assert(!isSliceOf(a, c));
+ assert(!isSliceOf(b, c));
+
+ assert(isSliceOf(d, a));
+ assert(isSliceOf(d, b));
+ assert(!isSliceOf(a, d));
+ assert(!isSliceOf(b, d));
+
+ assert(isSliceOf(e, a));
+ assert(isSliceOf(e, b));
+ assert(isSliceOf(e, c));
+ assert(isSliceOf(e, d));
+
+ //verifies R-value compatibilty
+ assert(!isSliceOf(a[$ .. $], a));
+ assert(isSliceOf(a[0 .. 0], a));
+ assert(isSliceOf(a, a[0.. $]));
+ assert(isSliceOf(a[0 .. $], a));
+ }
+}
+
@safe pure nothrow unittest
{
static void test(L, R)(L l, R r)
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
index ca83785..c6d5fca 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
@@ -40,6 +40,11 @@ longer used. It does so by destroying empty allocators. However, in order to
avoid thrashing (excessive creation/destruction of allocators under certain use
patterns), it keeps unused allocators for a while.
+The shared version of `AllocatorList` is `SharedAllocatorList`, which has
+identical semantics to its single-threaded version. Both `BookkeepingAllocator`
+and `Allocator` provided by `factoryFunction` must be shared, in order to
+ensure corectness.
+
Params:
factoryFunction = A function or template function (including function literals).
New allocators are created by calling `factoryFunction(n)` with strictly
@@ -661,65 +666,285 @@ version (Posix) @system unittest
assert(b1.length == 1024 * 10);
}
+/// Ditto
+shared struct SharedAllocatorList(Factory, BookkeepingAllocator = GCAllocator)
+{
+ import std.typecons : Ternary;
+ import std.traits : hasMember;
+ import core.internal.spinlock : SpinLock;
+
+private:
+ // Forward all calls to 'impl' and protect them by the lock below
+ AllocatorList!(Factory, BookkeepingAllocator) impl;
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+
+ // This could be potentially removed in the future,
+ // should a successor to <https://github.com/dlang/druntime/pull/2156>
+ // or a solution to <https://github.com/dlang/dmd/issues/17128> get merged.
+ static ref T assumeUnshared(T)(ref shared T val) @trusted @nogc pure nothrow
+ {
+ return *cast(T*) &val;
+ }
+
+ // Debug function used for testing
+ version (unittest)
+ auto allocators()
+ {
+ return impl.allocators;
+ }
+
+// Everything is inherited from the 'AllocatorList' implementation
+public:
+
+ /*
+ Note: This does not work well with rvalues because it copies them once more.
+ Probably not a problem here because all parameters are cheap.
+ <https://github.com/dlang/phobos/pull/6465/files#r189629862>
+ */
+
+ /**
+ The alignment offered.
+ */
+ enum alignment = impl.alignment;
+
+ /**
+ Allocate a block of size `s`. First tries to allocate from the existing
+ list of already-created allocators. If neither can satisfy the request,
+ creates a new allocator by calling `make(s)` and delegates the request
+ to it. However, if the allocation fresh off a newly created allocator
+ fails, subsequent calls to `allocate` will not cause more calls to $(D
+ make).
+ */
+ static if (hasMember!(typeof(impl), "allocate"))
+ void[] allocate(size_t s)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).allocate(s);
+ }
+
+ /**
+ Allocate a block of size `s` with alignment `a`. First tries to allocate
+ from the existing list of already-created allocators. If neither can
+ satisfy the request, creates a new allocator by calling `make(s + a - 1)`
+ and delegates the request to it. However, if the allocation fresh off a
+ newly created allocator fails, subsequent calls to `alignedAllocate`
+ will not cause more calls to `make`.
+ */
+ static if (hasMember!(typeof(impl), "alignedAllocate"))
+ void[] alignedAllocate(size_t s, uint a)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).alignedAllocate(s, a);
+ }
+
+ /**
+ Defined if `Allocator.deallocate` and `Allocator.owns` are defined.
+ */
+ static if (hasMember!(typeof(impl), "deallocate"))
+ bool deallocate(void[] b)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).deallocate(b);
+ }
+
+ /**
+ Defined only if `Allocator` defines `owns`. Tries each allocator in
+ turn, in most-recently-used order. If the owner is found, it is moved to
+ the front of the list as a side effect under the assumption it will be used
+ soon.
+
+ Returns: `Ternary.yes` if one allocator was found to return `Ternary.yes`,
+ `Ternary.no` if all component allocators returned `Ternary.no`, and
+ `Ternary.unknown` if no allocator returned `Ternary.yes` and at least one
+ returned `Ternary.unknown`.
+ */
+ static if (hasMember!(typeof(impl), "owns"))
+ Ternary owns(void[] b)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).owns(b);
+ }
+
+ /**
+ Defined only if `Allocator.expand` is defined. Finds the owner of `b`
+ and calls `expand` for it. The owner is not brought to the head of the
+ list.
+ */
+ static if (hasMember!(typeof(impl), "expand"))
+ bool expand(ref void[] b, size_t delta)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).expand(b, delta);
+ }
+
+ /**
+ Defined only if `Allocator.reallocate` is defined. Finds the owner of
+ `b` and calls `reallocate` for it. If that fails, calls the global
+ `reallocate`, which allocates a new block and moves memory.
+ */
+ static if (hasMember!(typeof(impl), "reallocate"))
+ bool reallocate(ref void[] b, size_t s)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).reallocate(b, s);
+ }
+
+ /**
+ Defined only if `Allocator.owns` and `Allocator.deallocateAll` are
+ defined.
+ */
+ static if (hasMember!(typeof(impl), "deallocateAll"))
+ bool deallocateAll()
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).deallocateAll();
+ }
+
+ /**
+ Returns `Ternary.yes` if no allocators are currently active,
+ `Ternary.no` otherwise. This methods never returns `Ternary.unknown`.
+ */
+ static if (hasMember!(typeof(impl), "empty"))
+ Ternary empty()
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return assumeUnshared(impl).empty();
+ }
+}
+
+/// Ditto
+template SharedAllocatorList(alias factoryFunction,
+ BookkeepingAllocator = GCAllocator)
+{
+ alias A = typeof(factoryFunction(1));
+ static assert(
+ // is a template function (including literals)
+ is(typeof({A function(size_t) @system x = factoryFunction!size_t;}))
+ ||
+ // or a function (including literals)
+ is(typeof({A function(size_t) @system x = factoryFunction;}))
+ ,
+ "Only function names and function literals that take size_t"
+ ~ " and return an allocator are accepted, not "
+ ~ typeof(factoryFunction).stringof
+ );
+ static struct Factory
+ {
+ A opCall(size_t n) { return factoryFunction(n); }
+ }
+ alias SharedAllocatorList = .SharedAllocatorList!(Factory, BookkeepingAllocator);
+}
+
@system unittest
{
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : Region, SharedRegion;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ const b1 = a.allocate(1024 * 8192);
+ assert(b1 !is null); // still works due to overdimensioning
+ const b2 = a.allocate(1024 * 10);
+ assert(b2.length == 1024 * 10);
+ a.deallocateAll();
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]),
- NullAllocator) a;
- const b1 = a.allocate(1024 * 8192);
- assert(b1 !is null); // still works due to overdimensioning
- const b2 = a.allocate(1024 * 10);
- assert(b2.length == 1024 * 10);
- a.deallocateAll();
+ NullAllocator) reg1;
+
+ SharedAllocatorList!((n) => SharedRegion!GCAllocator(new ubyte[max(n, 1024 * 4096)]),
+ NullAllocator) reg2;
+
+ testAlloc(reg1);
+ testAlloc(reg2);
}
@system unittest
{
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b1 = a.alignedAllocate(1024 * 8192, 1024);
- assert(b1 !is null); // still works due to overdimensioning
- assert(b1.length == 1024 * 8192);
- assert(b1.ptr.alignedAt(1024));
- assert(a.allocators.length == 1);
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
- b1 = a.alignedAllocate(0, 1024);
- assert(b1.length == 0);
- assert(a.allocators.length == 1);
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.alignedAllocate(1024 * 8192, 1024);
+ assert(b1 !is null); // still works due to overdimensioning
+ assert(b1.length == 1024 * 8192);
+ assert(b1.ptr.alignedAt(1024));
+ assert(a.allocators.length == 1);
- b1 = a.allocate(1024 * 10);
- assert(b1.length == 1024 * 10);
+ b1 = a.alignedAllocate(0, 1024);
+ assert(b1.length == 0);
+ assert(a.allocators.length == 1);
- assert(a.reallocate(b1, 1024));
- assert(b1.length == 1024);
+ b1 = a.allocate(1024 * 10);
+ assert(b1.length == 1024 * 10);
- a.deallocateAll();
+ assert(a.reallocate(b1, 1024));
+ assert(b1.length == 1024);
+
+ a.deallocateAll();
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
{
import core.exception : AssertError;
import std.exception : assertThrown;
-
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b1 = a.alignedAllocate(0, 1);
- assert(b1 is null);
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.alignedAllocate(0, 1);
+ assert(b1 is null);
+
+ b1 = a.alignedAllocate(1, 0);
+ assert(b1 is null);
- b1 = a.alignedAllocate(1, 0);
- assert(b1 is null);
+ b1 = a.alignedAllocate(0, 0);
+ assert(b1 is null);
- b1 = a.alignedAllocate(0, 0);
- assert(b1 is null);
+ assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024));
- assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024));
- a.deallocateAll();
+ // FIXME: This special-casing might note be necessary.
+ // At the moment though, this call would take potentially forever
+ // for the `SharedAllocatorList` from below.
+ static if (!is(Allocator == shared))
+ {
+ a.deallocateAll();
+ }
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
@@ -728,52 +953,74 @@ version (Posix) @system unittest
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b0 = a.alignedAllocate(1, 1024);
- assert(b0.length == 1);
- assert(b0.ptr.alignedAt(1024));
- assert(a.allocators.length == 1);
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
- auto b1 = a.alignedAllocate(1024 * 4096, 1024);
- assert(b1.length == 1024 * 4096);
- assert(b1.ptr.alignedAt(1024));
- assert(a.allocators.length == 2);
-
- auto b2 = a.alignedAllocate(1024, 128);
- assert(b2.length == 1024);
- assert(b2.ptr.alignedAt(128));
- assert(a.allocators.length == 2);
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b0 = a.alignedAllocate(1, 1024);
+ assert(b0.length == 1);
+ assert(b0.ptr.alignedAt(1024));
+ assert(a.allocators.length == 1);
+
+ auto b1 = a.alignedAllocate(1024 * 4096, 1024);
+ assert(b1.length == 1024 * 4096);
+ assert(b1.ptr.alignedAt(1024));
+ assert(a.allocators.length == 2);
+
+ auto b2 = a.alignedAllocate(1024, 128);
+ assert(b2.length == 1024);
+ assert(b2.ptr.alignedAt(128));
+ assert(a.allocators.length == 2);
+
+ auto b3 = a.allocate(1024);
+ assert(b3.length == 1024);
+ assert(a.allocators.length == 2);
+
+ auto b4 = a.allocate(1024 * 4096);
+ assert(b4.length == 1024 * 4096);
+ assert(a.allocators.length == 3);
+
+ static if (!is(Allocator == shared))
+ {
+ assert(a.root.empty == Ternary.no);
+ assert(a.deallocate(b4));
+ assert(a.root.empty == Ternary.yes);
- auto b3 = a.allocate(1024);
- assert(b3.length == 1024);
- assert(a.allocators.length == 2);
+ assert(a.deallocate(b1));
+ }
- auto b4 = a.allocate(1024 * 4096);
- assert(b4.length == 1024 * 4096);
- assert(a.allocators.length == 3);
+ a.deallocateAll();
+ }
- assert(a.root.empty == Ternary.no);
- assert(a.deallocate(b4));
- assert(a.root.empty == Ternary.yes);
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
- assert(a.deallocate(b1));
- a.deallocateAll();
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
{
- // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
- import std.experimental.allocator.building_blocks.region : BorrowedRegion;
- AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b1 = a.allocate(1024 * 8192);
- assert(b1 !is null); // still works due to overdimensioning
- b1 = a.allocate(1024 * 10);
- assert(b1.length == 1024 * 10);
- assert(a.reallocate(b1, 1024));
- assert(b1.length == 1024);
- a.deallocateAll();
+ import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.allocate(1024 * 8192);
+ assert(b1 !is null); // still works due to overdimensioning
+ b1 = a.allocate(1024 * 10);
+ assert(b1.length == 1024 * 10);
+ assert(a.reallocate(b1, 1024));
+ assert(b1.length == 1024);
+ a.deallocateAll();
+ }
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+ SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
@@ -798,24 +1045,33 @@ version (Posix) @system unittest
@system unittest
{
- import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.region : Region, SharedRegion;
enum bs = GCAllocator.alignment;
- AllocatorList!((n) => Region!GCAllocator(256 * bs)) a;
- auto b1 = a.allocate(192 * bs);
- assert(b1.length == 192 * bs);
- assert(a.allocators.length == 1);
- auto b2 = a.allocate(64 * bs);
- assert(b2.length == 64 * bs);
- assert(a.allocators.length == 1);
- auto b3 = a.allocate(192 * bs);
- assert(b3.length == 192 * bs);
- assert(a.allocators.length == 2);
- // Ensure deallocate inherits from parent allocators
- () nothrow @nogc { a.deallocate(b1); }();
- b1 = a.allocate(64 * bs);
- assert(b1.length == 64 * bs);
- assert(a.allocators.length == 2);
- a.deallocateAll();
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ auto b1 = a.allocate(192 * bs);
+ assert(b1.length == 192 * bs);
+ assert(a.allocators.length == 1);
+ auto b2 = a.allocate(64 * bs);
+ assert(b2.length == 64 * bs);
+ assert(a.allocators.length == 1);
+ auto b3 = a.allocate(192 * bs);
+ assert(b3.length == 192 * bs);
+ assert(a.allocators.length == 2);
+ // Ensure deallocate inherits from parent allocators
+ () nothrow @nogc { a.deallocate(b1); }();
+ b1 = a.allocate(64 * bs);
+ assert(b1.length == 64 * bs);
+ assert(a.allocators.length == 2);
+ a.deallocateAll();
+ }
+
+ AllocatorList!((n) => Region!GCAllocator(256 * bs)) a1;
+ SharedAllocatorList!((n) => SharedRegion!GCAllocator(256 * bs)) a2;
+
+ testAlloc(a1);
+ testAlloc(a2);
}
@system unittest
@@ -878,11 +1134,15 @@ version (Posix) @system unittest
@system unittest
{
- import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.building_blocks.ascending_page_allocator :
+ AscendingPageAllocator, SharedAscendingPageAllocator;
import std.experimental.allocator.mallocator : Mallocator;
import std.algorithm.comparison : max;
import std.typecons : Ternary;
+ enum pageSize = 4096;
+ enum numPages = 2;
+
static void testrw(void[] b)
{
ubyte* buf = cast(ubyte*) b.ptr;
@@ -893,53 +1153,56 @@ version (Posix) @system unittest
}
}
- enum numPages = 2;
- AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ void[] b1 = a.allocate(1);
+ assert(b1.length == 1);
+ b1 = a.allocate(2);
+ assert(b1.length == 2);
+ testrw(b1);
- void[] b1 = a.allocate(1);
- assert(b1.length == 1);
- b1 = a.allocate(2);
- assert(b1.length == 2);
- testrw(b1);
+ void[] b2 = a.allocate((numPages + 1) * pageSize);
+ assert(b2.length == (numPages + 1) * pageSize);
+ testrw(b2);
- void[] b2 = a.allocate((numPages + 1) * pageSize);
- assert(b2.length == (numPages + 1) * pageSize);
- testrw(b2);
+ void[] b3 = a.allocate(3);
+ assert(b3.length == 3);
+ testrw(b3);
- void[] b3 = a.allocate(3);
- assert(b3.length == 3);
- testrw(b3);
+ void[] b4 = a.allocate(0);
+ assert(b4.length == 0);
- void[] b4 = a.allocate(0);
- assert(b4.length == 0);
+ assert(a.allocators.length == 3);
+ assert(a.owns(b1) == Ternary.yes);
+ assert(a.owns(b2) == Ternary.yes);
+ assert(a.owns(b3) == Ternary.yes);
- assert(a.allocators.length == 3);
- assert(a.owns(b1) == Ternary.yes);
- assert(a.owns(b2) == Ternary.yes);
- assert(a.owns(b3) == Ternary.yes);
+ assert(a.expand(b1, pageSize - b1.length));
+ assert(b1.length == pageSize);
+ assert(!a.expand(b1, 1));
+ assert(!a.expand(b2, 1));
- assert(a.expand(b1, pageSize - b1.length));
- assert(b1.length == pageSize);
- assert(!a.expand(b1, 1));
- assert(!a.expand(b2, 1));
+ testrw(b1);
+ testrw(b2);
+ testrw(b3);
- testrw(b1);
- testrw(b2);
- testrw(b3);
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
- assert(a.deallocate(b1));
- assert(a.deallocate(b2));
+ const alignment = cast(uint) (70 * pageSize);
+ b3 = a.alignedAllocate(70 * pageSize, alignment);
+ assert(b3.length == 70 * pageSize);
+ assert(b3.ptr.alignedAt(alignment));
+ testrw(b3);
+ assert(a.allocators.length == 4);
+ assert(a.deallocate(b3));
- const alignment = cast(uint) (70 * pageSize);
- b3 = a.alignedAllocate(70 * pageSize, alignment);
- assert(b3.length == 70 * pageSize);
- assert(b3.ptr.alignedAt(alignment));
- testrw(b3);
- assert(a.allocators.length == 4);
- assert(a.deallocate(b3));
+ assert(a.deallocateAll());
+ }
- assert(a.deallocateAll());
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a1;
+ SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a2;
}
@system unittest
@@ -998,3 +1261,109 @@ version (Posix) @system unittest
assert(a.deallocateAll());
}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.algorithm.comparison : max;
+ import std.typecons : Ternary;
+
+ enum pageSize = 4096;
+
+ static void testrw(void[] b)
+ {
+ ubyte* buf = cast(ubyte*) b.ptr;
+ for (int i = 0; i < b.length; i += pageSize)
+ {
+ buf[i] = cast(ubyte) (i % 256);
+ assert(buf[i] == cast(ubyte) (i % 256));
+ }
+ }
+
+ enum numPages = 5;
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+ auto b = a.alignedAllocate(1, pageSize * 2);
+ assert(b.length == 1);
+ assert(a.expand(b, 4095));
+ assert(b.ptr.alignedAt(2 * 4096));
+ assert(b.length == 4096);
+
+ b = a.allocate(4096);
+ assert(b.length == 4096);
+ assert(a.allocators.length == 1);
+
+ assert(a.allocate(4096 * 5).length == 4096 * 5);
+ assert(a.allocators.length == 2);
+
+ assert(a.deallocateAll());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : SharedRegion;
+ import core.thread : ThreadGroup;
+ import std.algorithm.comparison : max;
+
+ enum numThreads = 10;
+ SharedAllocatorList!((n) => SharedRegion!(GCAllocator)(new ubyte[max(n, 1024)])) a;
+
+ void fun()
+ {
+ void[] b1 = a.allocate(1024);
+ assert(b1.length == 1024);
+
+ void[] b2 = a.alignedAllocate(1024, 1024);
+ assert(b2.length == 1024);
+ assert(b2.ptr.alignedAt(1024));
+
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ assert(a.deallocateAll());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator;
+ import core.thread : ThreadGroup;
+ import std.algorithm.comparison : max;
+
+ enum numThreads = 100;
+ enum pageSize = 4096;
+ enum numPages = 10;
+ SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, pageSize * numPages)), Mallocator) a;
+
+ void fun()
+ {
+ void[] b1 = a.allocate(512);
+ assert(b1.length == 512);
+ assert(a.expand(b1, 512));
+ assert(b1.length == 1024);
+
+ void[] b2 = a.alignedAllocate(1024, 4096);
+ assert(b2.length == 1024);
+ assert(b2.ptr.alignedAt(1024));
+
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ assert(a.deallocateAll());
+}
diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d
index e02c120..5dadce0 100644
--- a/libphobos/src/std/format/package.d
+++ b/libphobos/src/std/format/package.d
@@ -147,7 +147,7 @@ limited to a $(B '-') flag.
$(H4 $(LNAME2 format-indicator, Format Indicator))
The $(I format indicator) can either be a single character or an
-expression surrounded by $(B %\() and $(B %\)). It specifies the
+expression surrounded by $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). It specifies the
basic manner in which a value will be formatted and is the minimum
requirement to format a value.
@@ -187,7 +187,7 @@ $(BOOKTABLE ,
The $(I compound indicator) can be used to describe compound types
like arrays or structs in more detail. A compound type is enclosed
-within $(B '%\(') and $(B '%\)'). The enclosed sub-format string is
+within $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). The enclosed sub-format string is
applied to individual elements. The trailing portion of the
sub-format string following the specifier for the element is
interpreted as the delimiter, and is therefore omitted following the
diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d
index 5022702..cf7e3ff 100644
--- a/libphobos/src/std/functional.d
+++ b/libphobos/src/std/functional.d
@@ -1289,6 +1289,15 @@ alias pipe(fun...) = compose!(Reverse!(fun));
assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5);
}
+private template getOverloads(alias fun)
+{
+ import std.meta : AliasSeq;
+ static if (__traits(compiles, __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true)))
+ alias getOverloads = __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true);
+ else
+ alias getOverloads = AliasSeq!fun;
+}
+
/**
* $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as
* to avoid repeated computation. The memoization structure is a hash table keyed by a
@@ -1324,87 +1333,131 @@ Note:
*/
template memoize(alias fun)
{
- import std.traits : ReturnType;
- // https://issues.dlang.org/show_bug.cgi?id=13580
- // alias Args = Parameters!fun;
+ import std.traits : Parameters;
+ import std.meta : anySatisfy;
+
+ // Specific overloads:
+ alias overloads = getOverloads!fun;
+ static foreach (fn; overloads)
+ static if (is(Parameters!fn))
+ alias memoize = impl!(Parameters!fn);
+
+ enum isTemplate(alias a) = __traits(isTemplate, a);
+ static if (anySatisfy!(isTemplate, overloads))
+ {
+ // Generic implementation
+ alias memoize = impl;
+ }
- ReturnType!fun memoize(Parameters!fun args)
+ auto impl(Args...)(Args args) if (is(typeof(fun(args))))
{
- alias Args = Parameters!fun;
- import std.typecons : Tuple;
+ import std.typecons : Tuple, tuple;
import std.traits : Unqual;
- static Unqual!(ReturnType!fun)[Tuple!Args] memo;
- auto t = Tuple!Args(args);
- if (auto p = t in memo)
- return *p;
- auto r = fun(args);
- memo[t] = r;
- return r;
+ static if (args.length > 0)
+ {
+ static Unqual!(typeof(fun(args)))[Tuple!(typeof(args))] memo;
+
+ auto t = Tuple!Args(args);
+ if (auto p = t in memo)
+ return *p;
+ auto r = fun(args);
+ memo[t] = r;
+ return r;
+ }
+ else
+ {
+ static typeof(fun(args)) result;
+ result = fun(args);
+ return result;
+ }
}
}
/// ditto
template memoize(alias fun, uint maxSize)
{
- import std.traits : ReturnType;
- // https://issues.dlang.org/show_bug.cgi?id=13580
- // alias Args = Parameters!fun;
- ReturnType!fun memoize(Parameters!fun args)
+ import std.traits : Parameters;
+ import std.meta : anySatisfy;
+
+ // Specific overloads:
+ alias overloads = getOverloads!fun;
+ static foreach (fn; overloads)
+ static if (is(Parameters!fn))
+ alias memoize = impl!(Parameters!fn);
+
+ enum isTemplate(alias a) = __traits(isTemplate, a);
+ static if (anySatisfy!(isTemplate, overloads))
{
- import std.meta : staticMap;
- import std.traits : hasIndirections, Unqual;
- import std.typecons : tuple;
- static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; }
- static Value[] memo;
- static size_t[] initialized;
+ // Generic implementation
+ alias memoize = impl;
+ }
- if (!memo.length)
+ auto impl(Args...)(Args args) if (is(typeof(fun(args))))
+ {
+ static if (args.length > 0)
{
- import core.memory : GC;
+ import std.meta : staticMap;
+ import std.traits : hasIndirections, Unqual;
+ import std.typecons : tuple;
+ alias returnType = typeof(fun(args));
+ static struct Value { staticMap!(Unqual, Args) args; Unqual!returnType res; }
+ static Value[] memo;
+ static size_t[] initialized;
- // Ensure no allocation overflows
- static assert(maxSize < size_t.max / Value.sizeof);
- static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));
+ if (!memo.length)
+ {
+ import core.memory : GC;
- enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
- memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
- enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
- initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
- }
+ // Ensure no allocation overflows
+ static assert(maxSize < size_t.max / Value.sizeof);
+ static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));
- import core.bitop : bt, bts;
- import core.lifetime : emplace;
+ enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
+ memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
+ enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
+ initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
+ }
- size_t hash;
- foreach (ref arg; args)
- hash = hashOf(arg, hash);
- // cuckoo hashing
- immutable idx1 = hash % maxSize;
- if (!bt(initialized.ptr, idx1))
- {
- emplace(&memo[idx1], args, fun(args));
- // only set to initialized after setting args and value
- // https://issues.dlang.org/show_bug.cgi?id=14025
- bts(initialized.ptr, idx1);
+ import core.bitop : bt, bts;
+ import core.lifetime : emplace;
+
+ size_t hash;
+ foreach (ref arg; args)
+ hash = hashOf(arg, hash);
+ // cuckoo hashing
+ immutable idx1 = hash % maxSize;
+ if (!bt(initialized.ptr, idx1))
+ {
+ emplace(&memo[idx1], args, fun(args));
+ // only set to initialized after setting args and value
+ // https://issues.dlang.org/show_bug.cgi?id=14025
+ bts(initialized.ptr, idx1);
+ return memo[idx1].res;
+ }
+ else if (memo[idx1].args == args)
+ return memo[idx1].res;
+ // FNV prime
+ immutable idx2 = (hash * 16_777_619) % maxSize;
+ if (!bt(initialized.ptr, idx2))
+ {
+ emplace(&memo[idx2], memo[idx1]);
+ bts(initialized.ptr, idx2);
+ }
+ else if (memo[idx2].args == args)
+ return memo[idx2].res;
+ else if (idx1 != idx2)
+ memo[idx2] = memo[idx1];
+
+ memo[idx1] = Value(args, fun(args));
return memo[idx1].res;
}
- else if (memo[idx1].args == args)
- return memo[idx1].res;
- // FNV prime
- immutable idx2 = (hash * 16_777_619) % maxSize;
- if (!bt(initialized.ptr, idx2))
+ else
{
- emplace(&memo[idx2], memo[idx1]);
- bts(initialized.ptr, idx2);
+ static typeof(fun(args)) result;
+ result = fun(args);
+ return result;
}
- else if (memo[idx2].args == args)
- return memo[idx2].res;
- else if (idx1 != idx2)
- memo[idx2] = memo[idx1];
-
- memo[idx1] = Value(args, fun(args));
- return memo[idx1].res;
}
}
@@ -1464,6 +1517,37 @@ unittest
assert(fact(10) == 3628800);
}
+// Issue 20099
+@system unittest // not @safe due to memoize
+{
+ int i = 3;
+ alias a = memoize!((n) => i + n);
+ alias b = memoize!((n) => i + n, 3);
+
+ assert(a(3) == 6);
+ assert(b(3) == 6);
+}
+
+@system unittest // not @safe due to memoize
+{
+ static Object objNum(int a) { return new Object(); }
+ assert(memoize!objNum(0) is memoize!objNum(0U));
+ assert(memoize!(objNum, 3)(0) is memoize!(objNum, 3)(0U));
+}
+
+@system unittest // not @safe due to memoize
+{
+ struct S
+ {
+ static int fun() { return 0; }
+ static int fun(int i) { return 1; }
+ }
+ assert(memoize!(S.fun)() == 0);
+ assert(memoize!(S.fun)(3) == 1);
+ assert(memoize!(S.fun, 3)() == 0);
+ assert(memoize!(S.fun, 3)(3) == 1);
+}
+
@system unittest // not @safe due to memoize
{
import core.math : sqrt;
@@ -1626,6 +1710,19 @@ unittest
}}
}
+// memoize should continue to work with functions that cannot be evaluated at compile time
+@system unittest
+{
+ __gshared string[string] glob;
+
+ static bool foo()
+ {
+ return (":-)" in glob) is null;
+ }
+
+ assert(memoize!foo);
+}
+
private struct DelegateFaker(F)
{
import std.typecons : FuncInfo, MemberFunctionGenerator;
@@ -1717,13 +1814,45 @@ if (isCallable!(F))
{
return fp;
}
+ else static if (is(F Func == Func*) && is(Func == function))
+ {
+ return function(ref F fp) @trusted
+ {
+ return buildDelegate(fp);
+ }(fp);
+ }
else static if (is(typeof(&F.opCall) == delegate)
|| (is(typeof(&F.opCall) V : V*) && is(V == function)))
{
return toDelegate(&fp.opCall);
}
+ else static if (is(typeof(&fp.opCall!())))
+ {
+ return toDelegate(&fp.opCall!());
+ }
else
{
+ static assert(false, "Unsupported type of callable, please open an issue.");
+ }
+}
+
+///
+@safe unittest
+{
+ static int inc(ref uint num) {
+ num++;
+ return 8675309;
+ }
+
+ uint myNum = 0;
+ auto incMyNumDel = toDelegate(&inc);
+ auto returnVal = incMyNumDel(myNum);
+ assert(myNum == 1);
+}
+
+private template buildDelegate(F)
+{
+ auto buildDelegate(auto ref F fp) {
alias DelType = typeof(&(new DelegateFaker!(F)).doIt);
static struct DelegateFields {
@@ -1753,21 +1882,22 @@ if (isCallable!(F))
}
}
-///
-@system unittest
+@safe unittest
{
static int inc(ref uint num) {
num++;
return 8675309;
}
- uint myNum = 0;
- auto incMyNumDel = toDelegate(&inc);
- auto returnVal = incMyNumDel(myNum);
- assert(myNum == 1);
+ uint myNum = 0x1337;
+ struct S1 { int opCall() { inc(myNum); return myNum; } }
+ static assert(!is(typeof(&s1.opCall) == delegate));
+ S1 s1;
+ auto getvals1 = toDelegate(s1);
+ assert(getvals1() == 0x1338);
}
-@system unittest // not @safe due to toDelegate
+@system unittest
{
static int inc(ref uint num) {
num++;
@@ -1852,6 +1982,36 @@ if (isCallable!(F))
}
}
+
+@safe unittest
+{
+ static struct S1 { static void opCall()() { } }
+ static struct S2 { static T opCall(T = int)(T x) {return x; } }
+
+ S1 i1;
+ const dg1 = toDelegate(i1);
+ dg1();
+
+ S2 i2;
+ assert(toDelegate(i2)(0xBED) == 0xBED);
+}
+
+@safe unittest
+{
+ static void fun() @system pure nothrow @nogc
+ {
+ return;
+ }
+
+ auto fn = &fun;
+ static assert( is(typeof(fn) == void function() @system pure nothrow @nogc));
+ static assert(!is(typeof(fn) == void function() @safe pure nothrow @nogc));
+
+ auto dg = fn.toDelegate();
+ static assert( is(typeof(dg) == void delegate() @system pure nothrow @nogc));
+ static assert(!is(typeof(dg) == void delegate() @safe pure nothrow @nogc));
+}
+
/**
* Passes the fields of a struct as arguments to a function.
*
diff --git a/libphobos/src/std/logger/filelogger.d b/libphobos/src/std/logger/filelogger.d
index 5ba167c..754175c 100644
--- a/libphobos/src/std/logger/filelogger.d
+++ b/libphobos/src/std/logger/filelogger.d
@@ -276,7 +276,7 @@ class FileLogger : Logger
{
// we don't need to actually run the code, only make sure
// it compiles
- static _() {
+ static void _() {
auto l = new shared FileLogger("");
}
}
diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d
index 215ca20..5a84749 100644
--- a/libphobos/src/std/logger/package.d
+++ b/libphobos/src/std/logger/package.d
@@ -17,7 +17,7 @@ The easiest way to create a log message is to write:
import std.logger;
void main() {
- log("Hello World");
+ info("Hello World");
}
-------------
This will print a message to the `stderr` device. The message will contain
@@ -59,7 +59,7 @@ $(UL
$(LI `fatal`)
)
The default `Logger` will by default log to `stderr` and has a default
-`LogLevel` of `LogLevel.all`. The default Logger can be accessed by
+`LogLevel` of `LogLevel.info`. The default Logger can be accessed by
using the property called `sharedLog`. This property is a reference to the
current default `Logger`. This reference can be used to assign a new
default `Logger`.
diff --git a/libphobos/src/std/math/traits.d b/libphobos/src/std/math/traits.d
index 81ab1b7..8aba6a6 100644
--- a/libphobos/src/std/math/traits.d
+++ b/libphobos/src/std/math/traits.d
@@ -21,6 +21,7 @@ module std.math.traits;
import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned;
+
/*********************************
* Determines if $(D_PARAM x) is NaN.
* Params:
@@ -466,8 +467,27 @@ if (isFloatingPoint!(X))
*/
bool isIdentical(real x, real y) @trusted pure nothrow @nogc
{
- import std.math.traits : floatTraits, RealFormat;
-
+ if (__ctfe)
+ {
+ if (x !is y) return false;
+ if (x == x) return true; // If not NaN `is` implies identical representation.
+ static if (double.mant_dig != real.mant_dig)
+ {
+ // Works because we are in CTFE and there is no way in CTFE to set more
+ // bits of NaN payload than can fit in a double, and since 2.087
+ // changed real.init to be non-signaling I *think* there is no way in
+ // CTFE for a real to be a signaling NaN unless real and double have
+ // the same representation so real's bits can be manipulated directly.
+ double d1 = x, d2 = y;
+ }
+ else
+ {
+ // Alias to avoid converting signaling to quiet.
+ alias d1 = x;
+ alias d2 = y;
+ }
+ return *cast(long*) &d1 == *cast(long*) &d2;
+ }
// We're doing a bitwise comparison so the endianness is irrelevant.
long* pxs = cast(long *)&x;
long* pys = cast(long *)&y;
@@ -491,20 +511,44 @@ bool isIdentical(real x, real y) @trusted pure nothrow @nogc
assert(0, "isIdentical not implemented");
}
}
-
///
@safe @nogc pure nothrow unittest
{
+ // We're forcing the CTFE to run by assigning the result of the function to an enum
+ enum test1 = isIdentical(1.0,1.0);
+ enum test2 = isIdentical(real.nan,real.nan);
+ enum test3 = isIdentical(real.infinity, real.infinity);
+ enum test4 = isIdentical(real.infinity, real.infinity);
+ enum test5 = isIdentical(0.0, 0.0);
+
+ assert(test1);
+ assert(test2);
+ assert(test3);
+ assert(test4);
+ assert(test5);
+
+ enum test6 = !isIdentical(0.0, -0.0);
+ enum test7 = !isIdentical(real.nan, -real.nan);
+ enum test8 = !isIdentical(real.infinity, -real.infinity);
+
+ assert(test6);
+ assert(test7);
+ assert(test8);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ assert( !isIdentical(1.2,1.3));
assert( isIdentical(0.0, 0.0));
assert( isIdentical(1.0, 1.0));
assert( isIdentical(real.infinity, real.infinity));
assert( isIdentical(-real.infinity, -real.infinity));
+ assert( isIdentical(real.nan, real.nan));
assert(!isIdentical(0.0, -0.0));
assert(!isIdentical(real.nan, -real.nan));
assert(!isIdentical(real.infinity, -real.infinity));
}
-
/*********************************
* Return 1 if sign bit of e is set, 0 if not.
*/
diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d
index 9dae2a6..5fb3156 100644
--- a/libphobos/src/std/path.d
+++ b/libphobos/src/std/path.d
@@ -311,7 +311,6 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||
@safe unittest
{
import std.array;
- import std.utf : byDchar;
assert(rtrimDirSeparators("//abc//").array == "//abc");
assert(rtrimDirSeparators("//abc//"d).array == "//abc"d);
@@ -329,7 +328,6 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||
@safe unittest
{
import std.array;
- import std.utf : byDchar;
assert(trimDirSeparators("//abc//").array == "abc");
assert(trimDirSeparators("//abc//"d).array == "abc"d);
diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d
index 2efbcaa..1cb2264 100644
--- a/libphobos/src/std/process.d
+++ b/libphobos/src/std/process.d
@@ -2815,6 +2815,10 @@ void kill(Pid pid, int codeOrSignal)
else version (Posix)
{
import core.sys.posix.signal : kill;
+ if (pid.osHandle == Pid.invalid)
+ throw new ProcessException("Pid is invalid");
+ if (pid.osHandle == Pid.terminated)
+ throw new ProcessException("Pid is already terminated");
if (kill(pid.osHandle, codeOrSignal) == -1)
throw ProcessException.newFromErrno();
}
@@ -2856,7 +2860,7 @@ void kill(Pid pid, int codeOrSignal)
do { s = tryWait(pid); } while (!s.terminated);
version (Windows) assert(s.status == 123);
else version (Posix) assert(s.status == -SIGKILL);
- assertThrown!ProcessException(kill(pid));
+ assertThrown!ProcessException(kill(pid)); // Already terminated
}
@system unittest // wait() and kill() detached process
diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d
index e844297..b0d96f2 100644
--- a/libphobos/src/std/stdio.d
+++ b/libphobos/src/std/stdio.d
@@ -2409,13 +2409,13 @@ void main()
private struct ByLineCopy(Char, Terminator)
{
private:
- import std.typecons : RefCounted, RefCountedAutoInitialize;
+ import std.typecons : SafeRefCounted, RefCountedAutoInitialize;
/* Ref-counting stops the source range's ByLineCopyImpl
* from getting out of sync after the range is copied, e.g.
* when accessing range.front, then using std.range.take,
* then accessing range.front again. */
- alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
+ alias Impl = SafeRefCounted!(ByLineCopyImpl!(Char, Terminator),
RefCountedAutoInitialize.no);
Impl impl;
@@ -4626,7 +4626,7 @@ struct lines
f = File to read lines from.
terminator = Line separator (`'\n'` by default).
*/
- this(File f, dchar terminator = '\n')
+ this(File f, dchar terminator = '\n') @safe
{
this.f = f;
this.terminator = terminator;
@@ -4721,6 +4721,47 @@ struct lines
}
}
+@safe unittest
+{
+ /*
+ As pointed out in <https://github.com/dlang/phobos/issues/10605>,
+ it's a pity that `byLine()` & co. aren't @safe to use yet.
+
+ This is a first attempt at working towards that goal.
+ For now, this test doesn't do much; as there isn't much to do safely yet.
+ */
+ auto deleteMe = testFilename();
+ scope(exit) { imported!"std.file".remove(deleteMe); }
+
+ // Setup
+ {
+ auto f = File(deleteMe, "w");
+ scope(exit) { f.close(); }
+ foreach (i; 1 .. 11)
+ f.writeln(i);
+ }
+
+ // Actual tests
+ {
+ auto f = File(deleteMe, "r");
+ scope(exit) { f.close(); }
+
+ auto myLines = lines(f);
+ foreach (string line; myLines)
+ continue;
+
+ auto myByLineCopy = f.byLineCopy; // but cannot safely iterate yet
+ /*
+ still `@system`:
+ - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.empty`
+ - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.popFront`
+ - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.front`
+ */
+ //foreach (line; myByLineCopy)
+ // continue;
+ }
+}
+
@system unittest
{
static import std.file;