aboutsummaryrefslogtreecommitdiff
path: root/libphobos
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2023-07-09 22:08:36 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2023-07-09 22:08:36 +0200
commit3b007164b3ef114c3c86c42ca2455f8f2696fb0d (patch)
tree340037c67d4a2a57774103d54fa103390611f6e5 /libphobos
parentd6c1d7c4009bfe759719675ce3bc03ca503b9bf4 (diff)
downloadgcc-3b007164b3ef114c3c86c42ca2455f8f2696fb0d.zip
gcc-3b007164b3ef114c3c86c42ca2455f8f2696fb0d.tar.gz
gcc-3b007164b3ef114c3c86c42ca2455f8f2696fb0d.tar.bz2
d: Merge upstream dmd, druntime 28a3b24c2e, phobos 8ab95ded5.
D front-end changes: - Import dmd v2.104.0-beta.1. - Better error message when attribute inference fails down the call stack. - Using `;' as an empty statement has been turned into an error. - Using `in' parameters with non- `extern(D)' or `extern(C++)' functions is deprecated. - `in ref' on parameters has been deprecated in favor of `-preview=in'. - Throwing `immutable', `const', `inout', and `shared' qualified objects is now deprecated. - User Defined Attributes now parse Template Arguments. D runtime changes: - Import druntime v2.104.0-beta.1. Phobos changes: - Import phobos v2.104.0-beta.1. - Better static assert messages when instantiating `std.algorithm.comparison.clamp' with wrong inputs. - `std.typecons.Rebindable' now supports all types. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 28a3b24c2e. * dmd/VERSION: Bump version to v2.104.0-beta.1. * d-codegen.cc (build_bounds_slice_condition): Update for new front-end interface. * d-lang.cc (d_init_options): Likewise. (d_handle_option): Likewise. (d_post_options): Initialize global.compileEnv. * expr.cc (ExprVisitor::visit (CatExp *)): Replace code generation with new front-end lowering. (ExprVisitor::visit (LoweredAssignExp *)): New method. (ExprVisitor::visit (StructLiteralExp *)): Don't generate static initializer symbols for structs defined in C sources. * runtime.def (ARRAYCATT): Remove. (ARRAYCATNTX): Remove. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime 28a3b24c2e. * src/MERGE: Merge upstream phobos 8ab95ded5. gcc/testsuite/ChangeLog: * gdc.dg/rtti1.d: Move array concat testcase to ... * gdc.dg/nogc1.d: ... here. New test.
Diffstat (limited to 'libphobos')
-rw-r--r--libphobos/libdruntime/MERGE2
-rw-r--r--libphobos/libdruntime/core/atomic.d55
-rw-r--r--libphobos/libdruntime/core/demangle.d478
-rw-r--r--libphobos/libdruntime/core/internal/array/concatenation.d187
-rw-r--r--libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d4
-rw-r--r--libphobos/libdruntime/core/internal/string.d62
-rw-r--r--libphobos/libdruntime/core/stdc/config.d344
-rw-r--r--libphobos/libdruntime/core/sync/condition.d19
-rw-r--r--libphobos/libdruntime/core/thread/package.d39
-rw-r--r--libphobos/libdruntime/core/thread/types.d5
-rw-r--r--libphobos/libdruntime/core/time.d223
-rw-r--r--libphobos/libdruntime/gcc/sections/elf.d6
-rw-r--r--libphobos/libdruntime/object.d5
-rw-r--r--libphobos/libdruntime/rt/aApply.d24
-rw-r--r--libphobos/libdruntime/rt/aApplyR.d24
-rw-r--r--libphobos/libdruntime/rt/lifetime.d148
-rw-r--r--libphobos/libdruntime/rt/profilegc.d11
-rw-r--r--libphobos/src/MERGE2
-rw-r--r--libphobos/src/std/algorithm/comparison.d23
-rw-r--r--libphobos/src/std/algorithm/iteration.d28
-rw-r--r--libphobos/src/std/algorithm/searching.d68
-rw-r--r--libphobos/src/std/array.d187
-rw-r--r--libphobos/src/std/concurrency.d4
-rw-r--r--libphobos/src/std/container/dlist.d36
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/region.d20
-rw-r--r--libphobos/src/std/file.d4
-rw-r--r--libphobos/src/std/json.d44
-rw-r--r--libphobos/src/std/net/curl.d35
-rw-r--r--libphobos/src/std/path.d3
-rw-r--r--libphobos/src/std/random.d10
-rw-r--r--libphobos/src/std/range/package.d619
-rw-r--r--libphobos/src/std/range/primitives.d3
-rw-r--r--libphobos/src/std/regex/internal/backtracking.d7
-rw-r--r--libphobos/src/std/regex/internal/thompson.d7
-rw-r--r--libphobos/src/std/stdio.d50
-rw-r--r--libphobos/src/std/traits.d71
-rw-r--r--libphobos/src/std/typecons.d340
37 files changed, 2241 insertions, 956 deletions
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index 1205cd9..95ea67d 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
-a45f4e9f43e9fdbf0b666175e5e66b1ce4f561f6
+28a3b24c2e45de39cd3df528142fd06b6456e8fd
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
diff --git a/libphobos/libdruntime/core/atomic.d b/libphobos/libdruntime/core/atomic.d
index 4af3fdf..1fba06c 100644
--- a/libphobos/libdruntime/core/atomic.d
+++ b/libphobos/libdruntime/core/atomic.d
@@ -2,6 +2,9 @@
* The atomic module provides basic support for lock-free
* concurrent programming.
*
+ * $(NOTE Use the `-preview=nosharedaccess` compiler flag to detect
+ * unsafe individual read or write operations on shared data.)
+ *
* Copyright: Copyright Sean Kelly 2005 - 2016.
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Authors: Sean Kelly, Alex Rønne Petersen, Manu Evans
@@ -10,6 +13,22 @@
module core.atomic;
+///
+@safe unittest
+{
+ int y = 2;
+ shared int x = y; // OK
+
+ //x++; // read modify write error
+ x.atomicOp!"+="(1); // OK
+ //y = x; // read error with preview flag
+ y = x.atomicLoad(); // OK
+ assert(y == 3);
+ //x = 5; // write error with preview flag
+ x.atomicStore(5); // OK
+ assert(x.atomicLoad() == 5);
+}
+
import core.internal.atomic;
import core.internal.attributes : betterC;
import core.internal.traits : hasUnsharedIndirections;
@@ -1089,42 +1108,6 @@ version (CoreUnittest)
assert(ptr is null);
}
- unittest
- {
- import core.thread;
-
- // Use heap memory to ensure an optimizing
- // compiler doesn't put things in registers.
- uint* x = new uint();
- bool* f = new bool();
- uint* r = new uint();
-
- auto thr = new Thread(()
- {
- while (!*f)
- {
- }
-
- atomicFence();
-
- *r = *x;
- });
-
- thr.start();
-
- *x = 42;
-
- atomicFence();
-
- *f = true;
-
- atomicFence();
-
- thr.join();
-
- assert(*r == 42);
- }
-
// === atomicFetchAdd and atomicFetchSub operations ====
@betterC pure nothrow @nogc @safe unittest
{
diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d
index 3fcb266..f08e1f8 100644
--- a/libphobos/libdruntime/core/demangle.d
+++ b/libphobos/libdruntime/core/demangle.d
@@ -69,62 +69,17 @@ pure @safe:
{
buf = buf_;
addType = addType_;
- dst = dst_;
+ dst.dst = dst_;
}
-
- enum size_t minBufSize = 4000;
-
-
const(char)[] buf = null;
- char[] dst = null;
+ Buffer dst;
size_t pos = 0;
- size_t len = 0;
size_t brp = 0; // current back reference pos
AddType addType = AddType.yes;
bool mute = false;
Hooks hooks;
- static class ParseException : Exception
- {
- this(string msg) @safe pure nothrow
- {
- super( msg );
- }
- }
-
-
- static class OverflowException : Exception
- {
- this(string msg) @safe pure nothrow
- {
- super( msg );
- }
- }
-
-
- static noreturn error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */
- {
- pragma(inline, false); // tame dmd inliner
-
- //throw new ParseException( msg );
- debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
- throw __ctfe ? new ParseException(msg)
- : cast(ParseException) __traits(initSymbol, ParseException).ptr;
-
- }
-
-
- static noreturn overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */
- {
- pragma(inline, false); // tame dmd inliner
-
- //throw new OverflowException( msg );
- debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr );
- throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr;
- }
-
-
//////////////////////////////////////////////////////////////////////////
// Type Testing and Conversion
//////////////////////////////////////////////////////////////////////////
@@ -163,91 +118,11 @@ pure @safe:
error();
}
-
- //////////////////////////////////////////////////////////////////////////
- // Data Output
- //////////////////////////////////////////////////////////////////////////
-
-
- static bool contains( const(char)[] a, const(char)[] b ) @trusted
- {
- if (a.length && b.length)
- {
- auto bend = b.ptr + b.length;
- auto aend = a.ptr + a.length;
- return a.ptr <= b.ptr && bend <= aend;
- }
- return false;
- }
-
-
- // move val to the end of the dst buffer
- char[] shift( const(char)[] val )
- {
- pragma(inline, false); // tame dmd inliner
-
- if ( val.length && !mute )
- {
- assert( contains( dst[0 .. len], val ) );
- debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr );
-
- if (len + val.length > dst.length)
- overflow();
- size_t v = &val[0] - &dst[0];
- dst[len .. len + val.length] = val[];
- for (size_t p = v; p < len; p++)
- dst[p] = dst[p + val.length];
-
- return dst[len - val.length .. len];
- }
- return null;
- }
-
- // remove val from dst buffer
- void remove( const(char)[] val )
+ char[] shift(scope const(char)[] val) return scope
{
- pragma(inline, false); // tame dmd inliner
-
- if ( val.length )
- {
- assert( contains( dst[0 .. len], val ) );
- debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
- size_t v = &val[0] - &dst[0];
- assert( len >= val.length && len <= dst.length );
- len -= val.length;
- for (size_t p = v; p < len; p++)
- dst[p] = dst[p + val.length];
- }
- }
-
- char[] append( const(char)[] val ) return scope
- {
- pragma(inline, false); // tame dmd inliner
-
- if ( val.length && !mute )
- {
- if ( !dst.length )
- dst.length = minBufSize;
- assert( !contains( dst[0 .. len], val ) );
- debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
-
- if ( dst.length - len >= val.length && &dst[len] == &val[0] )
- {
- // data is already in place
- auto t = dst[len .. len + val.length];
- len += val.length;
- return t;
- }
- if ( dst.length - len >= val.length )
- {
- dst[len .. len + val.length] = val[];
- auto t = dst[len .. len + val.length];
- len += val.length;
- return t;
- }
- overflow();
- }
- return null;
+ if (mute)
+ return null;
+ return dst.shift(val);
}
void putComma(size_t n)
@@ -265,14 +140,9 @@ pure @safe:
void put(scope const(char)[] val) return scope
{
- pragma(inline, false); // tame dmd inliner
-
- if (!val.length) return;
-
- if (!contains(dst[0 .. len], val))
- append(val);
- else
- shift(val);
+ if (mute)
+ return;
+ dst.append(val);
}
@@ -297,7 +167,7 @@ pure @safe:
{
if ( val.length )
{
- append( " " );
+ put(" ");
put( val );
}
}
@@ -307,7 +177,7 @@ pure @safe:
{
debug(trace) printf( "silent+\n" );
debug(trace) scope(success) printf( "silent-\n" );
- auto n = len; dg(); len = n;
+ auto n = dst.length; dg(); dst.len = n;
}
@@ -835,7 +705,7 @@ pure @safe:
debug(trace) printf( "parseType+\n" );
debug(trace) scope(success) printf( "parseType-\n" );
- auto beg = len;
+ auto beg = dst.length;
auto t = front;
char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe
@@ -867,19 +737,19 @@ pure @safe:
put( "shared(" );
parseType();
put( ')' );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'x': // Const (x Type)
popFront();
put( "const(" );
parseType();
put( ')' );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'y': // Immutable (y Type)
popFront();
put( "immutable(" );
parseType();
put( ')' );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'N':
popFront();
switch ( front )
@@ -887,29 +757,28 @@ pure @safe:
case 'n': // Noreturn
popFront();
put("noreturn");
- return dst[beg .. len];
+ return dst[beg .. $];
case 'g': // Wild (Ng Type)
popFront();
// TODO: Anything needed here?
put( "inout(" );
parseType();
put( ')' );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'h': // TypeVector (Nh Type)
popFront();
put( "__vector(" );
parseType();
put( ')' );
- return dst[beg .. len];
+ return dst[beg .. $];
default:
error();
- assert( 0 );
}
case 'A': // TypeArray (A Type)
popFront();
parseType();
put( "[]" );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'G': // TypeStaticArray (G Number Type)
popFront();
auto num = sliceNumber();
@@ -917,21 +786,21 @@ pure @safe:
put( '[' );
put( num );
put( ']' );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'H': // TypeAssocArray (H Type Type)
popFront();
// skip t1
auto tx = parseType();
parseType();
put( '[' );
- put( tx );
+ shift(tx);
put( ']' );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'P': // TypePointer (P Type)
popFront();
parseType();
put( '*' );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction
return parseTypeFunction();
case 'C': // TypeClass (C LName)
@@ -940,7 +809,7 @@ pure @safe:
case 'T': // TypeTypedef (T LName)
popFront();
parseQualifiedName();
- return dst[beg .. len];
+ return dst[beg .. $];
case 'D': // TypeDelegate (D TypeFunction)
popFront();
auto modifiers = parseModifier();
@@ -957,15 +826,15 @@ pure @safe:
put(str);
}
}
- return dst[beg .. len];
+ return dst[beg .. $];
case 'n': // TypeNone (n)
popFront();
// TODO: Anything needed here?
- return dst[beg .. len];
+ return dst[beg .. $];
case 'B': // TypeTuple (B Number Arguments)
popFront();
// TODO: Handle this.
- return dst[beg .. len];
+ return dst[beg .. $];
case 'Z': // Internal symbol
// This 'type' is used for untyped internal symbols, i.e.:
// __array
@@ -975,13 +844,13 @@ pure @safe:
// __Interface
// __ModuleInfo
popFront();
- return dst[beg .. len];
+ return dst[beg .. $];
default:
if (t >= 'a' && t <= 'w')
{
popFront();
put( primitives[cast(size_t)(t - 'a')] );
- return dst[beg .. len];
+ return dst[beg .. $];
}
else if (t == 'z')
{
@@ -991,14 +860,13 @@ pure @safe:
case 'i':
popFront();
put( "cent" );
- return dst[beg .. len];
+ return dst[beg .. $];
case 'k':
popFront();
put( "ucent" );
- return dst[beg .. len];
+ return dst[beg .. $];
default:
error();
- assert( 0 );
}
}
error();
@@ -1351,12 +1219,13 @@ pure @safe:
{
debug(trace) printf( "parseTypeFunction+\n" );
debug(trace) scope(success) printf( "parseTypeFunction-\n" );
- auto beg = len;
+ auto beg = dst.length;
parseCallConvention();
auto attributes = parseFuncAttr();
- auto argbeg = len;
+ auto argbeg = dst.length;
+ put(IsDelegate.yes == isdg ? "delegate" : "function");
put( '(' );
parseFuncArguments();
put( ')' );
@@ -1369,17 +1238,18 @@ pure @safe:
put(str);
}
}
- auto retbeg = len;
- parseType();
- put( ' ' );
- // append delegate/function
- if (IsDelegate.yes == isdg)
- put( "delegate" );
- else
- put( "function" );
- // move arguments and attributes behind name
- shift( dst[argbeg .. retbeg] );
- return dst[beg..len];
+
+ // A function / delegate return type is located at the end of its mangling
+ // Write it in order, then shift it back to 'code order'
+ // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe'
+ {
+ auto retbeg = dst.length;
+ parseType();
+ put(' ');
+ shift(dst[argbeg .. retbeg]);
+ }
+
+ return dst[beg .. $];
}
static bool isCallConvention( char ch )
@@ -1701,7 +1571,7 @@ pure @safe:
if ( mayBeMangledNameArg() )
{
- auto l = len;
+ auto l = dst.length;
auto p = pos;
auto b = brp;
try
@@ -1712,7 +1582,7 @@ pure @safe:
}
catch ( ParseException e )
{
- len = l;
+ dst.len = l;
pos = p;
brp = b;
debug(trace) printf( "not a mangled name arg\n" );
@@ -1724,7 +1594,7 @@ pure @safe:
// try all possible pairs of numbers
auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName
pos--;
- auto l = len;
+ auto l = dst.length;
auto p = pos;
auto b = brp;
while ( qlen > 0 )
@@ -1740,7 +1610,7 @@ pure @safe:
}
qlen /= 10; // retry with one digit less
pos = --p;
- len = l;
+ dst.len = l;
brp = b;
}
}
@@ -1860,7 +1730,7 @@ pure @safe:
case '0': .. case '9':
if ( mayBeTemplateInstanceName() )
{
- auto t = len;
+ auto t = dst.length;
try
{
@@ -1871,7 +1741,7 @@ pure @safe:
catch ( ParseException e )
{
debug(trace) printf( "not a template instance name\n" );
- len = t;
+ dst.len = t;
}
}
goto case;
@@ -1889,10 +1759,9 @@ pure @safe:
{
// try to demangle a function, in case we are pointing to some function local
auto prevpos = pos;
- auto prevlen = len;
+ auto prevlen = dst.length;
auto prevbrp = brp;
- char[] attr;
try
{
if ( 'M' == front )
@@ -1908,6 +1777,7 @@ pure @safe:
}
if ( isCallConvention( front ) )
{
+ char[] attr;
// we don't want calling convention and attributes in the qualified name
parseCallConvention();
auto attributes = parseFuncAttr();
@@ -1917,23 +1787,23 @@ pure @safe:
put(str);
put(' ');
}
- attr = dst[prevlen .. len];
+ attr = dst[prevlen .. $];
}
put( '(' );
parseFuncArguments();
put( ')' );
+ return attr;
}
}
catch ( ParseException )
{
// not part of a qualified name, so back up
pos = prevpos;
- len = prevlen;
+ dst.len = prevlen;
brp = prevbrp;
- attr = null;
}
- return attr;
+ return null;
}
/*
@@ -1945,7 +1815,7 @@ pure @safe:
{
debug(trace) printf( "parseQualifiedName+\n" );
debug(trace) scope(success) printf( "parseQualifiedName-\n" );
- size_t beg = len;
+ size_t beg = dst.length;
size_t n = 0;
do
@@ -1956,7 +1826,7 @@ pure @safe:
parseFunctionTypeNoReturn();
} while ( isSymbolNameFront() );
- return dst[beg .. len];
+ return dst[beg .. $];
}
@@ -1977,17 +1847,17 @@ pure @safe:
match( 'D' );
do
{
- size_t beg = len;
- size_t nameEnd = len;
+ size_t beg = dst.length;
+ size_t nameEnd = dst.length;
char[] attr;
do
{
if ( attr )
- remove( attr ); // dump attributes of parent symbols
- if ( beg != len )
+ dst.remove(attr); // dump attributes of parent symbols
+ if (beg != dst.length)
put( '.' );
parseSymbolName();
- nameEnd = len;
+ nameEnd = dst.length;
attr = parseFunctionTypeNoReturn( displayType );
} while ( isSymbolNameFront() );
@@ -1995,7 +1865,7 @@ pure @safe:
if ( displayType )
{
attr = shift( attr );
- nameEnd = len - attr.length; // name includes function arguments
+ nameEnd = dst.length - attr.length; // name includes function arguments
}
name = dst[beg .. nameEnd];
@@ -2003,7 +1873,7 @@ pure @safe:
if ( 'M' == front )
popFront(); // has 'this' pointer
- auto lastlen = len;
+ auto lastlen = dst.length;
auto type = parseType();
if ( displayType )
{
@@ -2016,7 +1886,7 @@ pure @safe:
{
// remove type
assert( attr.length == 0 );
- len = lastlen;
+ dst.len = lastlen;
}
if ( pos >= buf.length || (n != 0 && pos >= end) )
return;
@@ -2040,15 +1910,6 @@ pure @safe:
parseMangledName( AddType.yes == addType );
}
- char[] copyInput() return scope
- {
- if (dst.length < buf.length)
- dst.length = buf.length;
- char[] r = dst[0 .. buf.length];
- r[] = buf[];
- return r;
- }
-
char[] doDemangle(alias FUNC)() return scope
{
while ( true )
@@ -2057,17 +1918,17 @@ pure @safe:
{
debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr );
FUNC();
- return dst[0 .. len];
+ return dst[0 .. $];
}
catch ( OverflowException e )
{
debug(trace) printf( "overflow... restarting\n" );
- auto a = minBufSize;
- auto b = 2 * dst.length;
+ auto a = Buffer.minSize;
+ auto b = 2 * dst.dst.length;
auto newsz = a < b ? b : a;
debug(info) printf( "growing dst to %lu bytes\n", newsz );
- dst.length = newsz;
- pos = len = brp = 0;
+ dst.dst.length = newsz;
+ pos = dst.len = brp = 0;
continue;
}
catch ( ParseException e )
@@ -2077,7 +1938,7 @@ pure @safe:
auto msg = e.toString();
printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
}
- return copyInput();
+ return dst.copyInput(buf);
}
catch ( Exception e )
{
@@ -2119,7 +1980,7 @@ char[] demangle(return scope const(char)[] buf, return scope char[] dst = null,
// fast path (avoiding throwing & catching exception) for obvious
// non-D mangled names
if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D"))
- return d.copyInput();
+ return d.dst.copyInput(buf);
return d.demangleName();
}
@@ -2212,7 +2073,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
d.popFront();
size_t n = d.decodeBackref();
if (!n || n > refpos)
- d.error("invalid back reference");
+ error("invalid back reference");
auto savepos = d.pos;
scope(exit) d.pos = savepos;
@@ -2220,11 +2081,11 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
auto idlen = d.decodeNumber();
if (d.pos + idlen > d.buf.length)
- d.error("invalid back reference");
+ error("invalid back reference");
auto id = d.buf[d.pos .. d.pos + idlen];
auto pid = id in idpos;
if (!pid)
- d.error("invalid back reference");
+ error("invalid back reference");
npos = positionInResult(*pid);
}
encodeBackref(reslen - npos);
@@ -2235,7 +2096,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
{
auto n = d.decodeNumber();
if (!n || n > d.buf.length || n > d.buf.length - d.pos)
- d.error("LName too shot or too long");
+ error("LName too shot or too long");
auto id = d.buf[d.pos .. d.pos + n];
d.pos += n;
if (auto pid = id in idpos)
@@ -2267,7 +2128,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
d.popFront();
auto n = d.decodeBackref();
if (n == 0 || n > refPos)
- d.error("invalid back reference");
+ error("invalid back reference");
size_t npos = positionInResult(refPos - n);
size_t reslen = result.length;
@@ -2903,6 +2764,7 @@ private shared CXX_DEMANGLER __cxa_demangle;
CXX_DEMANGLER getCXXDemangler() nothrow @trusted
{
+ import core.atomic : atomicLoad, atomicStore;
if (__cxa_demangle is null)
version (Posix)
{
@@ -2916,17 +2778,21 @@ CXX_DEMANGLER getCXXDemangler() nothrow @trusted
version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT;
if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle"))
- __cxa_demangle = found;
+ atomicStore(__cxa_demangle, found);
}
if (__cxa_demangle is null)
- __cxa_demangle = (const char* mangled_name, char* output_buffer,
- size_t* length, int* status) nothrow pure @trusted {
- *status = -1;
- return null;
- };
+ {
+ static extern(C) char* _(const char* mangled_name, char* output_buffer,
+ size_t* length, int* status) nothrow pure @trusted
+ {
+ *status = -1;
+ return null;
+ }
+ atomicStore(__cxa_demangle, &_);
+ }
- return __cxa_demangle;
+ return atomicLoad(__cxa_demangle);
}
/**
@@ -2967,3 +2833,163 @@ private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_d
dst[] = buf[];
return dst;
}
+
+/**
+ * Error handling through Exceptions
+ *
+ * The following types / functions are only used in this module,
+ * hence why the functions are `@trusted`.
+ * To make things `@nogc`, default-initialized instances are thrown.
+ */
+private class ParseException : Exception
+{
+ public this(string msg) @safe pure nothrow
+ {
+ super(msg);
+ }
+}
+
+/// Ditto
+private class OverflowException : Exception
+{
+ public this(string msg) @safe pure nothrow
+ {
+ super(msg);
+ }
+}
+
+/// Ditto
+private noreturn error(string msg = "Invalid symbol") @trusted pure
+{
+ pragma(inline, false); // tame dmd inliner
+
+ //throw new ParseException( msg );
+ debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
+ throw __ctfe ? new ParseException(msg)
+ : cast(ParseException) __traits(initSymbol, ParseException).ptr;
+}
+
+/// Ditto
+private noreturn overflow(string msg = "Buffer overflow") @trusted pure
+{
+ pragma(inline, false); // tame dmd inliner
+
+ //throw new OverflowException( msg );
+ debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr );
+ throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr;
+}
+
+private struct Buffer
+{
+ enum size_t minSize = 4000;
+
+ @safe pure:
+
+ private char[] dst;
+ private size_t len;
+
+ public alias opDollar = len;
+
+ public size_t length () const scope @safe pure nothrow @nogc
+ {
+ return this.len;
+ }
+
+ public inout(char)[] opSlice (size_t from, size_t to)
+ inout return scope @safe pure nothrow @nogc
+ {
+ assert(from <= to);
+ assert(to <= len);
+ return this.dst[from .. to];
+ }
+
+ static bool contains(scope const(char)[] a, scope const(char)[] b) @trusted
+ {
+ if (a.length && b.length)
+ {
+ auto bend = b.ptr + b.length;
+ auto aend = a.ptr + a.length;
+ return a.ptr <= b.ptr && bend <= aend;
+ }
+ return false;
+ }
+
+ char[] copyInput(scope const(char)[] buf)
+ return scope nothrow
+ {
+ if (dst.length < buf.length)
+ dst.length = buf.length;
+ char[] r = dst[0 .. buf.length];
+ r[] = buf[];
+ return r;
+ }
+
+ // move val to the end of the dst buffer
+ char[] shift(scope const(char)[] val) return scope
+ {
+ pragma(inline, false); // tame dmd inliner
+
+ if (val.length)
+ {
+ assert( contains( dst[0 .. len], val ) );
+ debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr );
+
+ if (len + val.length > dst.length)
+ overflow();
+ size_t v = &val[0] - &dst[0];
+ dst[len .. len + val.length] = val[];
+ for (size_t p = v; p < len; p++)
+ dst[p] = dst[p + val.length];
+
+ return dst[len - val.length .. len];
+ }
+ return null;
+ }
+
+ // remove val from dst buffer
+ void remove(scope const(char)[] val) scope
+ {
+ pragma(inline, false); // tame dmd inliner
+
+ if ( val.length )
+ {
+ assert( contains( dst[0 .. len], val ) );
+ debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
+ size_t v = &val[0] - &dst[0];
+ assert( len >= val.length && len <= dst.length );
+ len -= val.length;
+ for (size_t p = v; p < len; p++)
+ dst[p] = dst[p + val.length];
+ }
+ }
+
+ char[] append(scope const(char)[] val) return scope
+ {
+ pragma(inline, false); // tame dmd inliner
+
+ if (val.length)
+ {
+ if ( !dst.length )
+ dst.length = minSize;
+ assert( !contains( dst[0 .. len], val ) );
+ debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
+
+ if ( dst.length - len >= val.length && &dst[len] == &val[0] )
+ {
+ // data is already in place
+ auto t = dst[len .. len + val.length];
+ len += val.length;
+ return t;
+ }
+ if ( dst.length - len >= val.length )
+ {
+ dst[len .. len + val.length] = val[];
+ auto t = dst[len .. len + val.length];
+ len += val.length;
+ return t;
+ }
+ overflow();
+ }
+ return null;
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/array/concatenation.d b/libphobos/libdruntime/core/internal/array/concatenation.d
index 99f33da..ff777a6 100644
--- a/libphobos/libdruntime/core/internal/array/concatenation.d
+++ b/libphobos/libdruntime/core/internal/array/concatenation.d
@@ -8,71 +8,172 @@
*/
module core.internal.array.concatenation;
-/// See $(REF _d_arraycatnTX, rt,lifetime)
-private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) pure nothrow;
-
-/// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace`
-template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T)
+/**
+ * Concatenate the arrays inside of `froms`.
+ * `_d_arraycatnTX(a, b, c)` means `a ~ b ~ c`.
+ *
+ * Params:
+ * froms = Arrays to be concatenated.
+ * Returns:
+ * A newly allocated array that contains all the elements from `froms`.
+ */
+Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted
{
- private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!";
+ import core.internal.traits : hasElaborateCopyConstructor, Unqual;
+ import core.lifetime : copyEmplace;
+ import core.stdc.string : memcpy;
- /**
- * Concatenating the arrays inside of `arrs`.
- * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`.
- * Params:
- * arrs = Array containing arrays that will be concatenated.
- * Returns:
- * A newly allocated array that contains all the elements from all the arrays in `arrs`.
- * Bugs:
- * This function template was ported from a much older runtime hook that bypassed safety,
- * purity, and throwabilty checks. To prevent breaking existing code, this function template
- * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations.
- */
- ResultArrT _d_arraycatnTX(scope const Tarr arrs) @trusted pure nothrow
+ Tret res;
+ size_t totalLen;
+
+ alias T = typeof(res[0]);
+ enum elemSize = T.sizeof;
+ enum hasPostblit = __traits(hasPostblit, T);
+
+ static foreach (from; froms)
+ static if (is (typeof(from) : T))
+ totalLen++;
+ else
+ totalLen += from.length;
+
+ if (totalLen == 0)
+ return res;
+ res.length = totalLen;
+
+ /* Currently, if both a postblit and a cpctor are defined, the postblit is
+ * used. If this changes, the condition below will have to be adapted.
+ */
+ static if (hasElaborateCopyConstructor!T && !hasPostblit)
{
- pragma(inline, false);
- version (D_TypeInfo)
- {
- auto ti = typeid(ResultArrT);
+ size_t i = 0;
+ foreach (ref from; froms)
+ static if (is (typeof(from) : T))
+ copyEmplace(cast(T) from, res[i++]);
+ else
+ {
+ if (from.length)
+ foreach (ref elem; from)
+ copyEmplace(cast(T) elem, res[i++]);
+ }
+ }
+ else
+ {
+ auto resptr = cast(Unqual!T *) res;
+ foreach (ref from; froms)
+ static if (is (typeof(from) : T))
+ memcpy(resptr++, cast(Unqual!T *) &from, elemSize);
+ else
+ {
+ const len = from.length;
+ if (len)
+ {
+ memcpy(resptr, cast(Unqual!T *) from, len * elemSize);
+ resptr += len;
+ }
+ }
+
+ static if (hasPostblit)
+ foreach (ref elem; res)
+ (cast() elem).__xpostblit();
+ }
+
+ return res;
+}
- byte[][] arrs2 = (cast(byte[]*)arrs.ptr)[0 .. arrs.length];
- void[] result = ._d_arraycatnTX(ti, arrs2);
- return (cast(T*)result.ptr)[0 .. result.length];
+// postblit
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(this)
+ {
+ counter++;
}
- else
- assert(0, errorMessage);
}
- version (D_ProfileGC)
+ S[] arr1 = [S(0), S(1), S(2)];
+ S[] arr2 = [];
+ S[] arr3 = [S(6), S(7), S(8)];
+ S elem = S(9);
+ S[] result = _d_arraycatnTX!(S[])(arr1, arr2, arr3, elem);
+
+ assert(counter == 7);
+ assert(result == [S(0), S(1), S(2), S(6), S(7), S(8), S(9)]);
+}
+
+// copy constructor
+@safe unittest
+{
+ int counter;
+ struct S
{
- import core.internal.array.utils : _d_HookTraceImpl;
-
- /**
- * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concat).
- * Bugs:
- * This function template was ported from a much older runtime hook that bypassed safety,
- * purity, and throwabilty checks. To prevent breaking existing code, this function template
- * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations.
- */
- alias _d_arraycatnTXTrace = _d_HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage);
+ int val;
+ this(ref return scope S rhs)
+ {
+ val = rhs.val;
+ counter++;
+ }
}
+
+ S[] arr1 = [S(0), S(1), S(2)];
+ S[] arr2 = [S(3), S(4), S(5)];
+ S[] arr3 = [S(6), S(7), S(8)];
+ S elem = S(9);
+ S[] result = _d_arraycatnTX!(S[])(arr1, elem, arr2, arr3);
+
+ assert(counter == 10);
+ assert(result == [S(0), S(1), S(2), S(9), S(3), S(4), S(5), S(6), S(7), S(8)]);
}
+// throwing
@safe unittest
{
int counter;
+ bool didThrow;
struct S
{
int val;
this(this)
{
counter++;
+ if (counter == 4)
+ throw new Exception("");
}
}
- S[][] arr = [[S(0), S(1), S(2), S(3)], [S(4), S(5), S(6), S(7)]];
- S[] result = _d_arraycatnTXImpl!(typeof(arr))._d_arraycatnTX(arr);
+ try
+ {
+ S[] arr1 = [S(0), S(1), S(2)];
+ S[] arr2 = [S(3), S(4), S(5)];
+ _d_arraycatnTX!(S[])(arr1, arr2);
+ }
+ catch (Exception)
+ {
+ didThrow = true;
+ }
+
+ assert(counter == 4);
+ assert(didThrow);
+}
+
+version (D_ProfileGC)
+{
+ /**
+ * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concatenation).
+ */
+ Tret _d_arraycatnTXTrace(Tret, Tarr...)(string file, int line, string funcname, scope auto ref Tarr froms) @trusted
+ {
+ version (D_TypeInfo)
+ {
+ import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
+ mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX"));
- assert(counter == 8);
- assert(result == [S(0), S(1), S(2), S(3), S(4), S(5), S(6), S(7)]);
+ import core.lifetime: forward;
+ return _d_arraycatnTX!Tret(forward!froms);
+ }
+ else
+ assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!");
+ }
}
diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
index 6d19247..62ce941 100644
--- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
+++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
@@ -1509,7 +1509,7 @@ struct Gcx
List*[Bins.B_NUMSMALL] bucket; // free list for each small size
// run a collection when reaching those thresholds (number of used pages)
- float smallCollectThreshold, largeCollectThreshold;
+ float smallCollectThreshold = 0.0f, largeCollectThreshold = 0.0f;
uint usedSmallPages, usedLargePages;
// total number of mapped pages
uint mappedPages;
@@ -3529,7 +3529,7 @@ struct Pool
Small = 4,
Large = 12
}
- ShiftBy shiftBy; // shift count for the divisor used for determining bit indices.
+ ShiftBy shiftBy = void; // shift count for the divisor used for determining bit indices.
// This tracks how far back we have to go to find the nearest B_PAGE at
// a smaller address than a B_PAGEPLUS. To save space, we use a uint.
diff --git a/libphobos/libdruntime/core/internal/string.d b/libphobos/libdruntime/core/internal/string.d
index 64a9cc9..e09bba4 100644
--- a/libphobos/libdruntime/core/internal/string.d
+++ b/libphobos/libdruntime/core/internal/string.d
@@ -12,26 +12,57 @@ module core.internal.string;
pure:
nothrow:
@nogc:
+@safe:
-alias UnsignedStringBuf = char[20];
+alias UnsignedStringBuf = char[64];
/**
Converts an unsigned integer value to a string of characters.
-This implementation is a template so it can be used when compiling with -betterC.
+Can be used when compiling with -betterC. Does not allocate memory.
Params:
+ T = char, wchar or dchar
value = the unsigned integer value to convert
buf = the pre-allocated buffer used to store the result
- radix = the numeric base to use in the conversion (defaults to 10)
+ radix = the numeric base to use in the conversion 2 through 36 (defaults to 10)
+ upperCase = use upper case letters for radices 11 - 36
Returns:
The unsigned integer value as a string of characters
*/
-char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe
-if (radix >= 2 && radix <= 16)
+T[] unsignedToTempString(uint radix = 10, bool upperCase = false, T)(ulong value, return scope T[] buf)
+if (radix >= 2 && radix <= 36 &&
+ (is(T == char) || is(T == wchar) || is(T == dchar)))
{
+ enum baseChar = upperCase ? 'A' : 'a';
size_t i = buf.length;
+
+ static if (size_t.sizeof == 4) // 32 bit CPU
+ {
+ if (value <= uint.max)
+ {
+ // use faster 32 bit arithmetic
+ uint val = cast(uint) value;
+ do
+ {
+ uint x = void;
+ if (val < radix)
+ {
+ x = cast(uint)val;
+ val = 0;
+ }
+ else
+ {
+ x = cast(uint)(val % radix);
+ val /= radix;
+ }
+ buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar);
+ } while (val);
+ return buf[i .. $];
+ }
+ }
+
do
{
uint x = void;
@@ -45,7 +76,7 @@ if (radix >= 2 && radix <= 16)
x = cast(uint)(value % radix);
value /= radix;
}
- buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + 'a');
+ buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar);
} while (value);
return buf[i .. $];
}
@@ -73,7 +104,7 @@ Params:
Returns:
The unsigned integer value as a string of characters
*/
-auto unsignedToTempString(uint radix = 10)(ulong value) @safe
+auto unsignedToTempString(uint radix = 10)(ulong value)
{
// Need a buffer of 65 bytes for radix of 2 with room for
// signedToTempString to possibly add a negative sign.
@@ -85,11 +116,12 @@ auto unsignedToTempString(uint radix = 10)(ulong value) @safe
unittest
{
- UnsignedStringBuf buf;
+ UnsignedStringBuf buf = void;
assert(0.unsignedToTempString(buf) == "0");
assert(1.unsignedToTempString(buf) == "1");
assert(12.unsignedToTempString(buf) == "12");
assert(0x12ABCF .unsignedToTempString!16(buf) == "12abcf");
+ assert(0x12ABCF .unsignedToTempString!(16, true)(buf) == "12ABCF");
assert(long.sizeof.unsignedToTempString(buf) == "8");
assert(uint.max.unsignedToTempString(buf) == "4294967295");
assert(ulong.max.unsignedToTempString(buf) == "18446744073709551615");
@@ -106,16 +138,17 @@ unittest
// test bad radices
assert(!is(typeof(100.unsignedToTempString!1(buf))));
assert(!is(typeof(100.unsignedToTempString!0(buf) == "")));
+ assert(!is(typeof(100.unsignedToTempString!37(buf) == "")));
}
-alias SignedStringBuf = char[20];
+alias SignedStringBuf = char[65];
-char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) @safe
+T[] signedToTempString(uint radix = 10, bool upperCase = false, T)(long value, return scope T[] buf)
{
bool neg = value < 0;
if (neg)
value = cast(ulong)-value;
- auto r = unsignedToTempString!radix(value, buf);
+ auto r = unsignedToTempString!(radix, upperCase)(value, buf);
if (neg)
{
// about to do a slice without a bounds check
@@ -126,7 +159,7 @@ char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf)
return r;
}
-auto signedToTempString(uint radix = 10)(long value) @safe
+auto signedToTempString(uint radix = 10)(long value)
{
bool neg = value < 0;
if (neg)
@@ -142,7 +175,7 @@ auto signedToTempString(uint radix = 10)(long value) @safe
unittest
{
- SignedStringBuf buf;
+ SignedStringBuf buf = void;
assert(0.signedToTempString(buf) == "0");
assert(1.signedToTempString(buf) == "1");
assert((-1).signedToTempString(buf) == "-1");
@@ -150,6 +183,7 @@ unittest
assert((-12).signedToTempString(buf) == "-12");
assert(0x12ABCF .signedToTempString!16(buf) == "12abcf");
assert((-0x12ABCF) .signedToTempString!16(buf) == "-12abcf");
+ assert((-0x12ABCF) .signedToTempString!(16, true)(buf) == "-12ABCF");
assert(long.sizeof.signedToTempString(buf) == "8");
assert(int.max.signedToTempString(buf) == "2147483647");
assert(int.min.signedToTempString(buf) == "-2147483648");
@@ -183,7 +217,7 @@ unittest
* Returns:
* number of digits
*/
-int numDigits(uint radix = 10)(ulong value) @safe if (radix >= 2 && radix <= 36)
+int numDigits(uint radix = 10)(ulong value) if (radix >= 2 && radix <= 36)
{
int n = 1;
while (1)
diff --git a/libphobos/libdruntime/core/stdc/config.d b/libphobos/libdruntime/core/stdc/config.d
index c85682e..ca833ea 100644
--- a/libphobos/libdruntime/core/stdc/config.d
+++ b/libphobos/libdruntime/core/stdc/config.d
@@ -281,11 +281,185 @@ else
alias cpp_ptrdiff_t = ptrdiff_t;
}
-// ABI layout of native complex types.
-private struct _Complex(T)
+/** ABI layout of native complex types.
+ */
+struct _Complex(T)
+ if (is(T == float) || is(T == double) || is(T == c_long_double))
{
- T re;
- T im;
+ T re = 0;
+ T im = 0;
+
+ // Construction
+/+ https://issues.dlang.org/show_bug.cgi?id=23788 dmd codegen problem with constructors and _Complex!float
+ this(_Complex!float c) { re = c.re; im = c.im; }
+ this(_Complex!double c) { re = c.re; im = c.im; }
+ this(_Complex!c_long_double c) { re = c.re; im = c.im; }
+
+ this(T re, T im) { this.re = re; this.im = im; }
+
+ this(T re) { this.re = re; this.im = 0; }
++/
+ // Cast
+ R opCast(R)()
+ if (is(R == _Complex!float) || is(R == _Complex!double) || is(R == _Complex!c_long_double))
+ {
+ return R(this.re, this.im);
+ }
+
+ // Assignment
+
+ ref _Complex opAssign(_Complex!float c) { re = c.re; im = c.im; return this; }
+ ref _Complex opAssign(_Complex!double c) { re = c.re; im = c.im; return this; }
+ ref _Complex opAssign(_Complex!c_long_double c) { re = c.re; im = c.im; return this; }
+
+ ref _Complex opAssign(T t) { re = t; im = 0; return this; }
+
+ // Equals
+
+ bool opEquals(_Complex!float c) { return re == c.re && im == c.im; }
+ bool opEquals(_Complex!double c) { return re == c.re && im == c.im; }
+ bool opEquals(_Complex!c_long_double c) { return re == c.re && im == c.im; }
+
+ bool opEquals(T t) { return re == t && im == 0; }
+
+ // Unary operators
+
+ // +complex
+ _Complex opUnary(string op)()
+ if (op == "+")
+ {
+ return this;
+ }
+
+ // -complex
+ _Complex opUnary(string op)()
+ if (op == "-")
+ {
+ return _Complex(-re, -im);
+ }
+
+ // BINARY OPERATORS
+
+ // complex op complex
+ _Complex!(CommonType!(T,R)) opBinary(string op, R)(_Complex!R z)
+ {
+ alias C = typeof(return);
+ auto w = C(this.re, this.im);
+ return w.opOpAssign!(op)(z);
+ }
+
+ // complex op numeric
+ _Complex!(CommonType!(T,R)) opBinary(string op, R)(R r)
+ if (is(R : c_long_double))
+ {
+ alias C = typeof(return);
+ auto w = C(this.re, this.im);
+ return w.opOpAssign!(op)(r);
+ }
+
+ // numeric + complex, numeric * complex
+ _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r)
+ if ((op == "+" || op == "*") && is(R : c_long_double))
+ {
+ return opBinary!(op)(r);
+ }
+
+ // numeric - complex
+ _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r)
+ if (op == "-" && is(R : c_long_double))
+ {
+ return _Complex(r - re, -im);
+ }
+
+ // numeric / complex
+ _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r)
+ if (op == "/" && is(R : c_long_double))
+ {
+ import core.math : fabs;
+ typeof(return) w = void;
+ if (fabs(re) < fabs(im))
+ {
+ immutable ratio = re/im;
+ immutable rdivd = r/(re*ratio + im);
+
+ w.re = rdivd*ratio;
+ w.im = -rdivd;
+ }
+ else
+ {
+ immutable ratio = im/re;
+ immutable rdivd = r/(re + im*ratio);
+
+ w.re = rdivd;
+ w.im = -rdivd*ratio;
+ }
+
+ return w;
+ }
+
+ // OP-ASSIGN OPERATORS
+
+ // complex += complex, complex -= complex
+ ref _Complex opOpAssign(string op, C)(C z)
+ if ((op == "+" || op == "-") && is(C R == _Complex!R))
+ {
+ mixin ("re "~op~"= z.re;");
+ mixin ("im "~op~"= z.im;");
+ return this;
+ }
+
+ // complex *= complex
+ ref _Complex opOpAssign(string op, C)(C z)
+ if (op == "*" && is(C R == _Complex!R))
+ {
+ auto temp = re*z.re - im*z.im;
+ im = im*z.re + re*z.im;
+ re = temp;
+ return this;
+ }
+
+ // complex /= complex
+ ref _Complex opOpAssign(string op, C)(C z)
+ if (op == "/" && is(C R == _Complex!R))
+ {
+ import core.math : fabs;
+ if (fabs(z.re) < fabs(z.im))
+ {
+ immutable ratio = z.re/z.im;
+ immutable denom = z.re*ratio + z.im;
+
+ immutable temp = (re*ratio + im)/denom;
+ im = (im*ratio - re)/denom;
+ re = temp;
+ }
+ else
+ {
+ immutable ratio = z.im/z.re;
+ immutable denom = z.re + z.im*ratio;
+
+ immutable temp = (re + im*ratio)/denom;
+ im = (im - re*ratio)/denom;
+ re = temp;
+ }
+ return this;
+ }
+
+ // complex += numeric, complex -= numeric
+ ref _Complex opOpAssign(string op, U : T)(U a)
+ if (op == "+" || op == "-")
+ {
+ mixin ("re "~op~"= a;");
+ return this;
+ }
+
+ // complex *= numeric, complex /= numeric
+ ref _Complex opOpAssign(string op, U : T)(U a)
+ if (op == "*" || op == "/")
+ {
+ mixin ("re "~op~"= a;");
+ mixin ("im "~op~"= a;");
+ return this;
+ }
// Helper properties.
pragma(inline, true)
@@ -311,6 +485,168 @@ enum __c_complex_real : _Complex!c_long_double;
alias c_complex_float = __c_complex_float;
alias c_complex_double = __c_complex_double;
alias c_complex_real = __c_complex_real;
+
+private template CommonType(T, R)
+{
+ // Special kludge for Microsoft c_long_double
+ static if (is(T == c_long_double))
+ alias CommonType = T;
+ else static if (is(R == c_long_double))
+ alias CommonType = R;
+ else
+ alias CommonType = typeof(true ? T.init : R.init);
+}
+
+/************ unittests ****************/
+
+version (unittest)
+{
+ private:
+
+ alias _cfloat = _Complex!float;
+ alias _cdouble = _Complex!double;
+ alias _creal = _Complex!c_long_double;
+
+ T abs(T)(T t) => t < 0 ? -t : t;
+}
+
+@safe pure nothrow unittest
+{
+ auto c1 = _cdouble(1.0, 1.0);
+
+ // Check unary operations.
+ auto c2 = _cdouble(0.5, 2.0);
+
+ assert(c2 == +c2);
+
+ assert((-c2).re == -(c2.re));
+ assert((-c2).im == -(c2.im));
+ assert(c2 == -(-c2));
+
+ // Check complex-complex operations.
+ auto cpc = c1 + c2;
+ assert(cpc.re == c1.re + c2.re);
+ assert(cpc.im == c1.im + c2.im);
+
+ auto cmc = c1 - c2;
+ assert(cmc.re == c1.re - c2.re);
+ assert(cmc.im == c1.im - c2.im);
+
+ auto ctc = c1 * c2;
+ assert(ctc == _cdouble(-1.5, 2.5));
+
+ auto cdc = c1 / c2;
+ assert(abs(cdc.re - 0.5882352941177) < 1e-12);
+ assert(abs(cdc.im - -0.3529411764706) < 1e-12);
+
+ // Check complex-real operations.
+ double a = 123.456;
+
+ auto cpr = c1 + a;
+ assert(cpr.re == c1.re + a);
+ assert(cpr.im == c1.im);
+
+ auto cmr = c1 - a;
+ assert(cmr.re == c1.re - a);
+ assert(cmr.im == c1.im);
+
+ auto ctr = c1 * a;
+ assert(ctr.re == c1.re*a);
+ assert(ctr.im == c1.im*a);
+
+ auto cdr = c1 / a;
+ assert(abs(cdr.re - 0.00810005184033) < 1e-12);
+ assert(abs(cdr.im - 0.00810005184033) < 1e-12);
+
+ auto rpc = a + c1;
+ assert(rpc == cpr);
+
+ auto rmc = a - c1;
+ assert(rmc.re == a-c1.re);
+ assert(rmc.im == -c1.im);
+
+ auto rtc = a * c1;
+ assert(rtc == ctr);
+
+ auto rdc = a / c1;
+ assert(abs(rdc.re - 61.728) < 1e-12);
+ assert(abs(rdc.im - -61.728) < 1e-12);
+
+ rdc = a / c2;
+ assert(abs(rdc.re - 14.5242352941) < 1e-10);
+ assert(abs(rdc.im - -58.0969411765) < 1e-10);
+
+ // Check operations between different complex types.
+ auto cf = _cfloat(1.0, 1.0);
+ auto cr = _creal(1.0, 1.0);
+ auto c1pcf = c1 + cf;
+ auto c1pcr = c1 + cr;
+ static assert(is(typeof(c1pcf) == _cdouble));
+ static assert(is(typeof(c1pcr) == _creal));
+ assert(c1pcf.re == c1pcr.re);
+ assert(c1pcf.im == c1pcr.im);
+
+ auto c1c = c1;
+ auto c2c = c2;
+
+ c1c /= c1;
+ assert(abs(c1c.re - 1.0) < 1e-10);
+ assert(abs(c1c.im - 0.0) < 1e-10);
+
+ c1c = c1;
+ c1c /= c2;
+ assert(abs(c1c.re - 0.5882352941177) < 1e-12);
+ assert(abs(c1c.im - -0.3529411764706) < 1e-12);
+
+ c2c /= c1;
+ assert(abs(c2c.re - 1.25) < 1e-11);
+ assert(abs(c2c.im - 0.75) < 1e-12);
+
+ c2c = c2;
+ c2c /= c2;
+ assert(abs(c2c.re - 1.0) < 1e-11);
+ assert(abs(c2c.im - 0.0) < 1e-12);
+}
+
+@safe pure nothrow unittest
+{
+ // Initialization
+ _cdouble a = _cdouble(1, 0);
+ assert(a.re == 1 && a.im == 0);
+ _cdouble b = _cdouble(1.0, 0);
+ assert(b.re == 1.0 && b.im == 0);
+// _cdouble c = _creal(1.0, 2);
+// assert(c.re == 1.0 && c.im == 2);
+}
+
+@safe pure nothrow unittest
+{
+ // Assignments and comparisons
+ _cdouble z;
+
+ z = 1;
+ assert(z == 1);
+ assert(z.re == 1.0 && z.im == 0.0);
+
+ z = 2.0;
+ assert(z == 2.0);
+ assert(z.re == 2.0 && z.im == 0.0);
+
+ z = 1.0L;
+ assert(z == 1.0L);
+ assert(z.re == 1.0 && z.im == 0.0);
+
+ auto w = _creal(1.0, 1.0);
+ z = w;
+ assert(z == w);
+ assert(z.re == 1.0 && z.im == 1.0);
+
+ auto c = _cfloat(2.0, 2.0);
+ z = c;
+ assert(z == c);
+ assert(z.re == 2.0 && z.im == 2.0);
+}
+
}
diff --git a/libphobos/libdruntime/core/sync/condition.d b/libphobos/libdruntime/core/sync/condition.d
index ddd04ae..afcfd74 100644
--- a/libphobos/libdruntime/core/sync/condition.d
+++ b/libphobos/libdruntime/core/sync/condition.d
@@ -84,7 +84,8 @@ class Condition
/// ditto
this( shared Mutex m ) shared nothrow @safe @nogc
{
- this(m, true);
+ import core.atomic : atomicLoad;
+ this(atomicLoad(m), true);
}
//
@@ -117,7 +118,15 @@ class Condition
}
else version (Posix)
{
- m_assocMutex = m;
+ static if (is(Q == shared))
+ {
+ import core.atomic : atomicLoad;
+ m_assocMutex = atomicLoad(m);
+ }
+ else
+ {
+ m_assocMutex = m;
+ }
static if ( is( typeof( pthread_condattr_setclock ) ) )
{
() @trusted
@@ -183,7 +192,8 @@ class Condition
/// ditto
@property shared(Mutex) mutex() shared
{
- return m_assocMutex;
+ import core.atomic : atomicLoad;
+ return atomicLoad(m_assocMutex);
}
// undocumented function for internal use
@@ -195,7 +205,8 @@ class Condition
// ditto
final @property shared(Mutex) mutex_nothrow() shared pure nothrow @safe @nogc
{
- return m_assocMutex;
+ import core.atomic : atomicLoad;
+ return atomicLoad(m_assocMutex);
}
////////////////////////////////////////////////////////////////////////////
diff --git a/libphobos/libdruntime/core/thread/package.d b/libphobos/libdruntime/core/thread/package.d
index 71b0237..d81ebbd 100644
--- a/libphobos/libdruntime/core/thread/package.d
+++ b/libphobos/libdruntime/core/thread/package.d
@@ -18,3 +18,42 @@ public import core.thread.threadbase;
public import core.thread.threadgroup;
public import core.thread.types;
public import core.thread.context;
+
+
+// this test is here to avoid a cyclic dependency between
+// core.thread and core.atomic
+unittest
+{
+ import core.atomic;
+
+ // Use heap memory to ensure an optimizing
+ // compiler doesn't put things in registers.
+ uint* x = new uint();
+ bool* f = new bool();
+ uint* r = new uint();
+
+ auto thr = new Thread(()
+ {
+ while (!*f)
+ {
+ }
+
+ atomicFence();
+
+ *r = *x;
+ });
+
+ thr.start();
+
+ *x = 42;
+
+ atomicFence();
+
+ *f = true;
+
+ atomicFence();
+
+ thr.join();
+
+ assert(*r == 42);
+}
diff --git a/libphobos/libdruntime/core/thread/types.d b/libphobos/libdruntime/core/thread/types.d
index eb84ad7..998f610 100644
--- a/libphobos/libdruntime/core/thread/types.d
+++ b/libphobos/libdruntime/core/thread/types.d
@@ -41,8 +41,9 @@ version (GNU)
}
else
{
- // this should be true for most architectures
- enum isStackGrowingDown = true;
+ version (X86) enum isStackGrowingDown = true;
+ else version (X86_64) enum isStackGrowingDown = true;
+ else static assert(0, "It is undefined how the stack grows on this architecture.");
}
package
diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d
index 8d50875..be941e2a 100644
--- a/libphobos/libdruntime/core/time.d
+++ b/libphobos/libdruntime/core/time.d
@@ -18,7 +18,7 @@
$(LEADINGROW Types)
$(TR $(TDNW $(LREF Duration)) $(TD Represents a duration of time of weeks
or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds).))
- $(TR $(TDNW $(LREF TickDuration)) $(TD Represents a duration of time in
+ $(TR $(TDNW $(LREF TickDuration)) $(TD $(RED DEPRECATED) Represents a duration of time in
system clock ticks, using the highest precision that the system provides.))
$(TR $(TDNW $(LREF MonoTime)) $(TD Represents a monotonic timestamp in
system clock ticks, using the highest precision that the system provides.))
@@ -682,21 +682,21 @@ public:
$(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration))
$(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration))
$(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration))
- $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration))
- $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration))
)
Params:
rhs = The duration to add to or subtract from this $(D Duration).
+/
- Duration opBinary(string op, D)(D rhs) const nothrow @nogc
- if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) ||
- ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)))
+ Duration opBinary(string op)(const Duration rhs) const nothrow @nogc
+ if (op == "+" || op == "-" || op == "%")
{
- static if (is(immutable D == immutable Duration))
- return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs"));
- else
- return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs"));
+ return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs"));
+ }
+
+ deprecated Duration opBinary(string op)(const TickDuration rhs) const nothrow @nogc
+ if (op == "+" || op == "-")
+ {
+ return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs"));
}
version (CoreUnittest) unittest
@@ -733,7 +733,13 @@ public:
assert((cast(D)Duration(-7)) - (cast(E)Duration(-5)) == Duration(-2));
assert((cast(D)Duration(-7)) % (cast(E)Duration(5)) == Duration(-2));
}
+ }
+ }
+ version (CoreUnittest) deprecated unittest
+ {
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
+ {
foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
assertApprox((cast(D)Duration(5)) + cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80));
@@ -761,6 +767,8 @@ public:
/++
+ $(RED TickDuration is Deprecated)
+
Adds or subtracts two durations.
The legal types of arithmetic for $(D Duration) using this operator are
@@ -774,14 +782,14 @@ public:
lhs = The $(D TickDuration) to add to this $(D Duration) or to
subtract this $(D Duration) from.
+/
- Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc
+ deprecated Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc
if ((op == "+" || op == "-") &&
is(immutable D == immutable TickDuration))
{
return Duration(mixin("lhs.hnsecs " ~ op ~ " _hnsecs"));
}
- version (CoreUnittest) unittest
+ version (CoreUnittest) deprecated unittest
{
foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
@@ -821,21 +829,22 @@ public:
$(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration))
$(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration))
$(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration))
- $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration))
- $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration))
)
Params:
rhs = The duration to add to or subtract from this $(D Duration).
+/
- ref Duration opOpAssign(string op, D)(const scope D rhs) nothrow @nogc
- if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) ||
- ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)))
+ ref Duration opOpAssign(string op)(const Duration rhs) nothrow @nogc
+ if (op == "+" || op == "-" || op == "%")
{
- static if (is(immutable D == immutable Duration))
- mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;");
- else
- mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;");
+ mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;");
+ return this;
+ }
+
+ deprecated ref Duration opOpAssign(string op)(const TickDuration rhs) nothrow @nogc
+ if (op == "+" || op == "-")
+ {
+ mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;");
return this;
}
@@ -850,13 +859,6 @@ public:
throw new AssertError("op assign failed", __FILE__, line);
}
- static void test2(string op, E)
- (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__)
- {
- assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line);
- assertApprox(actual, lower, upper, "op assign failed", line);
- }
-
foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
{
test1!"+="(Duration(5), (cast(E)Duration(7)), Duration(12));
@@ -888,6 +890,26 @@ public:
test1!"%="(Duration(-7), (cast(E)Duration(-5)), Duration(-2));
}
+ foreach (D; AliasSeq!(const Duration, immutable Duration))
+ {
+ foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
+ {
+ D lhs = D(120);
+ E rhs = E(120);
+ static assert(!__traits(compiles, lhs += rhs), D.stringof ~ " " ~ E.stringof);
+ }
+ }
+ }
+
+ version (CoreUnittest) deprecated unittest
+ {
+ static void test2(string op, E)
+ (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__)
+ {
+ assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line);
+ assertApprox(actual, lower, upper, "op assign failed", line);
+ }
+
foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
test2!"+="(Duration(5), cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80));
@@ -913,8 +935,7 @@ public:
foreach (D; AliasSeq!(const Duration, immutable Duration))
{
- foreach (E; AliasSeq!(Duration, const Duration, immutable Duration,
- TickDuration, const TickDuration, immutable TickDuration))
+ foreach (E; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
D lhs = D(120);
E rhs = E(120);
@@ -1170,19 +1191,21 @@ public:
/++
+ $(RED TickDuration is Deprecated)
+
Returns a $(LREF TickDuration) with the same number of hnsecs as this
$(D Duration).
Note that the conventional way to convert between $(D Duration) and
$(D TickDuration) is using $(REF to, std,conv), e.g.:
$(D duration.to!TickDuration())
+/
- TickDuration opCast(T)() const nothrow @nogc
+ deprecated TickDuration opCast(T)() const nothrow @nogc
if (is(immutable T == immutable TickDuration))
{
return TickDuration.from!"hnsecs"(_hnsecs);
}
- version (CoreUnittest) unittest
+ version (CoreUnittest) deprecated unittest
{
foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
@@ -1762,6 +1785,8 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest
}
/++
+ $(RED TickDuration is DEPRECATED)
+
Converts a $(D TickDuration) to the given units as either an integral
value or a floating point value.
@@ -1773,6 +1798,7 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest
td = The TickDuration to convert
+/
+deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead")
T to(string units, T, D)(D td) @safe pure nothrow @nogc
if (is(immutable D == immutable TickDuration) &&
(units == "seconds" ||
@@ -1804,7 +1830,7 @@ T to(string units, T, D)(D td) @safe pure nothrow @nogc
}
///
-unittest
+deprecated unittest
{
auto t = TickDuration.from!"seconds"(1000);
@@ -1816,7 +1842,7 @@ unittest
assert(fabs(td - 1000) < 0.001);
}
-unittest
+deprecated unittest
{
void testFun(string U)() {
auto t1v = 1000;
@@ -2756,22 +2782,24 @@ unittest
/++
- $(RED Warning: TickDuration will be deprecated in the near future (once all
- uses of it in Phobos have been deprecated). Please use
+ $(RED Warning: TickDuration is deprecated. Please use
$(LREF MonoTime) for the cases where a monotonic timestamp is needed
and $(LREF Duration) when a duration is needed, rather than using
- TickDuration. It has been decided that TickDuration is too confusing
- (e.g. it conflates a monotonic timestamp and a duration in monotonic
- clock ticks) and that having multiple duration types is too awkward
- and confusing.)
+ TickDuration.)
Represents a duration of time in system clock ticks.
The system clock ticks are the ticks of the system clock at the highest
precision that the system provides.
+/
+deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead")
struct TickDuration
{
+deprecated:
+ private static TickDuration TDRvalueOf(TickDuration td)
+ {
+ return td;
+ }
/++
The number of ticks that the system clock has in one second.
@@ -2811,14 +2839,14 @@ struct TickDuration
version (CoreUnittest) unittest
{
- assert(zero == TickDuration(0));
- assert(TickDuration.max == TickDuration(long.max));
- assert(TickDuration.min == TickDuration(long.min));
- assert(TickDuration.min < TickDuration.zero);
- assert(TickDuration.zero < TickDuration.max);
- assert(TickDuration.min < TickDuration.max);
- assert(TickDuration.min - TickDuration(1) == TickDuration.max);
- assert(TickDuration.max + TickDuration(1) == TickDuration.min);
+ assert((zero == TickDuration(0)) == true);
+ assert((TickDuration.max == TickDuration(long.max)) == true);
+ assert((TickDuration.min == TickDuration(long.min)) == true);
+ assert((TickDuration.min < TickDuration.zero) == true);
+ assert((TickDuration.zero < TickDuration.max) == true);
+ assert((TickDuration.min < TickDuration.max) == true);
+ assert((TickDuration.min - TickDuration(1) == TickDuration.max) == true);
+ assert((TickDuration.max + TickDuration(1) == TickDuration.min) == true);
}
@@ -3040,12 +3068,12 @@ struct TickDuration
{
auto a = TickDuration.currSystemTick;
auto result = a += cast(T)TickDuration.currSystemTick;
- assert(a == result);
+ assert((a == result) == true);
assert(a.to!("seconds", real)() >= 0);
auto b = TickDuration.currSystemTick;
result = b -= cast(T)TickDuration.currSystemTick;
- assert(b == result);
+ assert((b == result) == true);
assert(b.to!("seconds", real)() <= 0);
foreach (U; AliasSeq!(const TickDuration, immutable TickDuration))
@@ -3104,11 +3132,11 @@ struct TickDuration
{
foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
- assert(-(cast(T)TickDuration(7)) == TickDuration(-7));
- assert(-(cast(T)TickDuration(5)) == TickDuration(-5));
- assert(-(cast(T)TickDuration(-7)) == TickDuration(7));
- assert(-(cast(T)TickDuration(-5)) == TickDuration(5));
- assert(-(cast(T)TickDuration(0)) == TickDuration(0));
+ assert((-(cast(T)TickDuration(7)) == TickDuration(-7)) == true);
+ assert((-(cast(T)TickDuration(5)) == TickDuration(-5)) == true);
+ assert((-(cast(T)TickDuration(-7)) == TickDuration(7)) == true);
+ assert((-(cast(T)TickDuration(-5)) == TickDuration(5)) == true);
+ assert((-(cast(T)TickDuration(0)) == TickDuration(0)) == true);
}
}
@@ -3130,9 +3158,9 @@ struct TickDuration
{
T t = TickDuration.currSystemTick;
U u = t;
- assert(t == u);
- assert(rvalueOf(t) == u);
- assert(t == rvalueOf(u));
+ assert((t == u) == true);
+ assert((TDRvalueOf(t) == u) == true);
+ assert((t == TDRvalueOf(u)) == true);
}
}
@@ -3142,20 +3170,20 @@ struct TickDuration
{
T t = TickDuration.currSystemTick;
U u = t + t;
- assert(t < u);
- assert(t <= t);
- assert(u > t);
- assert(u >= u);
-
- assert(rvalueOf(t) < u);
- assert(rvalueOf(t) <= t);
- assert(rvalueOf(u) > t);
- assert(rvalueOf(u) >= u);
-
- assert(t < rvalueOf(u));
- assert(t <= rvalueOf(t));
- assert(u > rvalueOf(t));
- assert(u >= rvalueOf(u));
+ assert((t < u) == true);
+ assert((t <= t) == true);
+ assert((u > t) == true);
+ assert((u >= u) == true);
+
+ assert((TDRvalueOf(t) < u) == true);
+ assert((TDRvalueOf(t) <= t) == true);
+ assert((TDRvalueOf(u) > t) == true);
+ assert((TDRvalueOf(u) >= u) == true);
+
+ assert((t < TDRvalueOf(u)) == true);
+ assert((t <= TDRvalueOf(t)) == true);
+ assert((u > TDRvalueOf(t)) == true);
+ assert((u >= TDRvalueOf(u)) == true);
}
}
}
@@ -3186,7 +3214,7 @@ struct TickDuration
TickDuration t1 = curr;
immutable t2 = curr + curr;
t1 *= 2;
- assert(t1 == t2);
+ assert((t1 == t2) == true);
t1 = curr;
t1 *= 2.0;
@@ -3195,7 +3223,7 @@ struct TickDuration
t1 = curr;
t1 *= 2.1;
- assert(t1 > t2);
+ assert((t1 > t2) == true);
foreach (T; AliasSeq!(const TickDuration, immutable TickDuration))
{
@@ -3237,7 +3265,7 @@ struct TickDuration
immutable t1 = curr;
TickDuration t2 = curr + curr;
t2 /= 2;
- assert(t1 == t2);
+ assert((t1 == t2) == true);
t2 = curr + curr;
t2 /= 2.0;
@@ -3246,7 +3274,7 @@ struct TickDuration
t2 = curr + curr;
t2 /= 2.1;
- assert(t1 > t2);
+ assert((t1 > t2) == true);
_assertThrown!TimeException(t2 /= 0);
@@ -3284,10 +3312,10 @@ struct TickDuration
{
T t1 = TickDuration.currSystemTick;
T t2 = t1 + t1;
- assert(t1 * 2 == t2);
+ assert((t1 * 2 == t2) == true);
immutable tol = TickDuration(cast(long)(_abs(t1.length) * double.epsilon * 2.0));
assertApprox(t1 * 2.0, t2 - tol, t2 + tol);
- assert(t1 * 2.1 > t2);
+ assert((t1 * 2.1 > t2) == true);
}
}
@@ -3323,12 +3351,12 @@ struct TickDuration
{
T t1 = TickDuration.currSystemTick;
T t2 = t1 + t1;
- assert(t2 / 2 == t1);
+ assert((t2 / 2 == t1) == true);
immutable tol = TickDuration(cast(long)(_abs(t2.length) * double.epsilon / 2.0));
assertApprox(t2 / 2.0, t1 - tol, t1 + tol);
- assert(t2 / 2.1 < t1);
+ assert((t2 / 2.1 < t1) == true);
- _assertThrown!TimeException(t2 / 0);
+ _assertThrownDep!TimeException(t2 / 0);
}
}
@@ -3430,7 +3458,6 @@ struct TickDuration
}
}
-
/++
Generic way of converting between two time units. Conversions to smaller
units use truncating division. Years and months can be converted to each
@@ -3641,6 +3668,7 @@ Duration abs(Duration duration) @safe pure nothrow @nogc
}
/++ Ditto +/
+deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead")
TickDuration abs(TickDuration duration) @safe pure nothrow @nogc
{
return TickDuration(_abs(duration.length));
@@ -3650,9 +3678,12 @@ unittest
{
assert(abs(dur!"msecs"(5)) == dur!"msecs"(5));
assert(abs(dur!"msecs"(-5)) == dur!"msecs"(5));
+}
- assert(abs(TickDuration(17)) == TickDuration(17));
- assert(abs(TickDuration(-17)) == TickDuration(17));
+deprecated unittest
+{
+ assert((abs(TickDuration(17)) == TickDuration(17)) == true);
+ assert((abs(TickDuration(-17)) == TickDuration(17)) == true);
}
@@ -3987,6 +4018,28 @@ unittest
}
}
+version (CoreUnittest) deprecated void _assertThrownDep(T : Throwable = Exception, E)
+ (lazy E expression,
+ string msg = null,
+ string file = __FILE__,
+ size_t line = __LINE__)
+{
+ bool thrown = false;
+
+ try
+ expression();
+ catch (T t)
+ thrown = true;
+
+ if (!thrown)
+ {
+ immutable tail = msg.length == 0 ? "." : ": " ~ msg;
+
+ throw new AssertError("assertThrown() failed: No " ~ T.stringof ~ " was thrown" ~ tail, file, line);
+ }
+}
+
+
version (CoreUnittest) void assertApprox(D, E)(D actual,
E lower,
@@ -4001,7 +4054,7 @@ version (CoreUnittest) void assertApprox(D, E)(D actual,
throw new AssertError(msg ~ ": upper: " ~ actual.toString(), __FILE__, line);
}
-version (CoreUnittest) void assertApprox(D, E)(D actual,
+version (CoreUnittest) deprecated void assertApprox(D, E)(D actual,
E lower,
E upper,
string msg = "unittest failure",
diff --git a/libphobos/libdruntime/gcc/sections/elf.d b/libphobos/libdruntime/gcc/sections/elf.d
index 0c7a771..5376957 100644
--- a/libphobos/libdruntime/gcc/sections/elf.d
+++ b/libphobos/libdruntime/gcc/sections/elf.d
@@ -737,7 +737,7 @@ version (Shared)
!pthread_mutex_unlock(&_handleToDSOMutex) || assert(0);
}
- void getDependencies(in ref dl_phdr_info info, ref Array!(DSO*) deps)
+ void getDependencies(const ref dl_phdr_info info, ref Array!(DSO*) deps)
{
// get the entries of the .dynamic section
ElfW!"Dyn"[] dyns;
@@ -833,7 +833,7 @@ version (Shared)
* Scan segments in Linux dl_phdr_info struct and store
* the TLS and writeable data segments in *pdso.
*/
-void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc
+void scanSegments(const ref dl_phdr_info info, DSO* pdso) nothrow @nogc
{
foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum])
{
@@ -957,7 +957,7 @@ bool findDSOInfoForAddr(in void* addr, dl_phdr_info* result=null) nothrow @nogc
* Determine if 'addr' lies within shared object 'info'.
* If so, return true and fill in 'result' with the corresponding ELF program header.
*/
-bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
+bool findSegmentForAddr(const ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc
{
if (addr < cast(void*)info.dlpi_addr) // less than base address of object means quick reject
return false;
diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d
index a77788b..610cb7a 100644
--- a/libphobos/libdruntime/object.d
+++ b/libphobos/libdruntime/object.d
@@ -4529,12 +4529,15 @@ public import core.internal.entrypoint : _d_cmain;
public import core.internal.array.appending : _d_arrayappendT;
version (D_ProfileGC)
+{
public import core.internal.array.appending : _d_arrayappendTTrace;
+ public import core.internal.array.concatenation : _d_arraycatnTXTrace;
+}
public import core.internal.array.appending : _d_arrayappendcTXImpl;
public import core.internal.array.comparison : __cmp;
public import core.internal.array.equality : __equals;
public import core.internal.array.casting: __ArrayCast;
-public import core.internal.array.concatenation : _d_arraycatnTXImpl;
+public import core.internal.array.concatenation : _d_arraycatnTX;
public import core.internal.array.construction : _d_arrayctor;
public import core.internal.array.construction : _d_arraysetctor;
public import core.internal.array.arrayassign : _d_arrayassign_l;
diff --git a/libphobos/libdruntime/rt/aApply.d b/libphobos/libdruntime/rt/aApply.d
index 5d5ddb3..c59d9dc 100644
--- a/libphobos/libdruntime/rt/aApply.d
+++ b/libphobos/libdruntime/rt/aApply.d
@@ -71,7 +71,7 @@ Params:
Returns:
non-zero when the loop was exited through a `break`
*/
-extern (C) int _aApplycd1(in char[] aa, dg_t dg)
+extern (C) int _aApplycd1(scope const(char)[] aa, dg_t dg)
{
int result;
size_t len = aa.length;
@@ -132,7 +132,7 @@ unittest
}
/// ditto
-extern (C) int _aApplywd1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplywd1(scope const(wchar)[] aa, dg_t dg)
{
int result;
size_t len = aa.length;
@@ -193,7 +193,7 @@ unittest
}
/// ditto
-extern (C) int _aApplycw1(in char[] aa, dg_t dg)
+extern (C) int _aApplycw1(scope const(char)[] aa, dg_t dg)
{
int result;
size_t len = aa.length;
@@ -267,7 +267,7 @@ unittest
}
/// ditto
-extern (C) int _aApplywc1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplywc1(scope const(wchar)[] aa, dg_t dg)
{
int result;
size_t len = aa.length;
@@ -347,7 +347,7 @@ unittest
}
/// ditto
-extern (C) int _aApplydc1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplydc1(scope const(dchar)[] aa, dg_t dg)
{
int result;
@@ -423,7 +423,7 @@ unittest
}
/// ditto
-extern (C) int _aApplydw1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplydw1(scope const(dchar)[] aa, dg_t dg)
{
int result;
@@ -508,7 +508,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c);
/**
Variants of _aApplyXXX that include a loop index.
*/
-extern (C) int _aApplycd2(in char[] aa, dg2_t dg)
+extern (C) int _aApplycd2(scope const(char)[] aa, dg2_t dg)
{
int result;
size_t len = aa.length;
@@ -576,7 +576,7 @@ unittest
}
/// ditto
-extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplywd2(scope const(wchar)[] aa, dg2_t dg)
{
int result;
size_t len = aa.length;
@@ -644,7 +644,7 @@ unittest
}
/// ditto
-extern (C) int _aApplycw2(in char[] aa, dg2_t dg)
+extern (C) int _aApplycw2(scope const(char)[] aa, dg2_t dg)
{
int result;
size_t len = aa.length;
@@ -723,7 +723,7 @@ unittest
}
/// ditto
-extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplywc2(scope const(wchar)[] aa, dg2_t dg)
{
int result;
size_t len = aa.length;
@@ -808,7 +808,7 @@ unittest
}
/// ditto
-extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplydc2(scope const(dchar)[] aa, dg2_t dg)
{
int result;
size_t len = aa.length;
@@ -888,7 +888,7 @@ unittest
}
/// ditto
-extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplydw2(scope const(dchar)[] aa, dg2_t dg)
{ int result;
debug(apply) printf("_aApplydw2(), len = %d\n", aa.length);
diff --git a/libphobos/libdruntime/rt/aApplyR.d b/libphobos/libdruntime/rt/aApplyR.d
index ce3bb9e..560025c 100644
--- a/libphobos/libdruntime/rt/aApplyR.d
+++ b/libphobos/libdruntime/rt/aApplyR.d
@@ -34,7 +34,7 @@ Params:
Returns:
non-zero when the loop was exited through a `break`
*/
-extern (C) int _aApplyRcd1(in char[] aa, dg_t dg)
+extern (C) int _aApplyRcd1(scope const(char)[] aa, dg_t dg)
{ int result;
debug(apply) printf("_aApplyRcd1(), len = %d\n", aa.length);
@@ -107,7 +107,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplyRwd1(scope const(wchar)[] aa, dg_t dg)
{ int result;
debug(apply) printf("_aApplyRwd1(), len = %d\n", aa.length);
@@ -170,7 +170,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRcw1(in char[] aa, dg_t dg)
+extern (C) int _aApplyRcw1(scope const(char)[] aa, dg_t dg)
{ int result;
debug(apply) printf("_aApplyRcw1(), len = %d\n", aa.length);
@@ -256,7 +256,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg)
+extern (C) int _aApplyRwc1(scope const(wchar)[] aa, dg_t dg)
{ int result;
debug(apply) printf("_aApplyRwc1(), len = %d\n", aa.length);
@@ -340,7 +340,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplyRdc1(scope const(dchar)[] aa, dg_t dg)
{ int result;
debug(apply) printf("_aApplyRdc1(), len = %d\n", aa.length);
@@ -418,7 +418,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg)
+extern (C) int _aApplyRdw1(scope const(dchar)[] aa, dg_t dg)
{ int result;
debug(apply) printf("_aApplyRdw1(), len = %d\n", aa.length);
@@ -502,7 +502,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c);
/**
Variants of _aApplyRXXX that include a loop index.
*/
-extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg)
+extern (C) int _aApplyRcd2(scope const(char)[] aa, dg2_t dg)
{ int result;
size_t i;
size_t len = aa.length;
@@ -578,7 +578,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplyRwd2(scope const(wchar)[] aa, dg2_t dg)
{ int result;
debug(apply) printf("_aApplyRwd2(), len = %d\n", aa.length);
@@ -643,7 +643,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg)
+extern (C) int _aApplyRcw2(scope const(char)[] aa, dg2_t dg)
{ int result;
debug(apply) printf("_aApplyRcw2(), len = %d\n", aa.length);
@@ -731,7 +731,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg)
+extern (C) int _aApplyRwc2(scope const(wchar)[] aa, dg2_t dg)
{ int result;
debug(apply) printf("_aApplyRwc2(), len = %d\n", aa.length);
@@ -817,7 +817,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplyRdc2(scope const(dchar)[] aa, dg2_t dg)
{ int result;
debug(apply) printf("_aApplyRdc2(), len = %d\n", aa.length);
@@ -896,7 +896,7 @@ unittest
}
/// ditto
-extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg)
+extern (C) int _aApplyRdw2(scope const(dchar)[] aa, dg2_t dg)
{ int result;
debug(apply) printf("_aApplyRdw2(), len = %d\n", aa.length);
diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d
index f2515c3..a37541b 100644
--- a/libphobos/libdruntime/rt/lifetime.d
+++ b/libphobos/libdruntime/rt/lifetime.d
@@ -1197,7 +1197,7 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
}
/// ditto
-extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
+extern (C) void* _d_newitemT(const TypeInfo _ti) pure nothrow @weak
{
import core.stdc.string;
auto p = _d_newitemU(_ti);
@@ -1206,7 +1206,7 @@ extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
}
/// Same as above, for item with non-zero initializer.
-extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak
+extern (C) void* _d_newitemiT(const TypeInfo _ti) pure nothrow @weak
{
import core.stdc.string;
auto p = _d_newitemU(_ti);
@@ -1286,7 +1286,7 @@ extern (C) CollectHandler rt_getCollectHandler()
/**
*
*/
-extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow
+extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope const(void)[] segment) nothrow
{
if (attr & BlkAttr.STRUCTFINAL)
{
@@ -2231,148 +2231,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
return x;
}
-
-/**
-Concatenate two arrays into a new array
-
----
-void main()
-{
- int[] x = [10, 20, 30];
- int[] y = [40, 50];
- int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]);
-}
----
-
-Params:
- ti = type that the two arrays share
- x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
- y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length
-Returns:
- resulting concatenated array, with `.length` equal to new element length despite `byte` type
-*/
-extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak
-out (result)
-{
- auto tinext = unqualify(ti.next);
- auto sizeelem = tinext.tsize; // array element size
- debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr);
- assert(result.length == x.length + y.length);
-
- // If a postblit is involved, the contents of result might rightly differ
- // from the bitwise concatenation of x and y.
- if (!hasPostblit(tinext))
- {
- for (size_t i = 0; i < x.length * sizeelem; i++)
- assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
- for (size_t i = 0; i < y.length * sizeelem; i++)
- assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]);
- }
-
- size_t cap = GC.sizeOf(result.ptr);
- assert(!cap || cap > result.length * sizeelem);
-}
-do
-{
- import core.stdc.string;
- version (none)
- {
- /* Cannot use this optimization because:
- * char[] a, b;
- * char c = 'a';
- * b = a ~ c;
- * c = 'b';
- * will change the contents of b.
- */
- if (!y.length)
- return x;
- if (!x.length)
- return y;
- }
-
- auto tinext = unqualify(ti.next);
- auto sizeelem = tinext.tsize; // array element size
- debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem);
- size_t xlen = x.length * sizeelem;
- size_t ylen = y.length * sizeelem;
- size_t len = xlen + ylen;
-
- if (!len)
- return null;
-
- auto info = __arrayAlloc(len, ti, tinext);
- byte* p = cast(byte*)__arrayStart(info);
- p[len] = 0; // guessing this is to optimize for null-terminated arrays?
- memcpy(p, x.ptr, xlen);
- memcpy(p + xlen, y.ptr, ylen);
- // do postblit processing
- __doPostblit(p, xlen + ylen, tinext);
-
- auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
- __setArrayAllocLength(info, len, isshared, tinext);
- return p[0 .. x.length + y.length];
-}
-
-
-/**
-Concatenate multiple arrays at once
-
-This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance.
-
-```
-void main()
-{
- int[] a, b, c;
- int[] res = a ~ b ~ c;
- // _d_arraycatnTX(typeid(int[]),
- // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]);
-}
-```
-
-Params:
- ti = type of arrays to concatenate and resulting array
- arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same
-
-Returns:
- newly created concatenated array, `.length` equal to the total element length despite `void` type
-*/
-extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
-{
- import core.stdc.string;
-
- size_t length;
- auto tinext = unqualify(ti.next);
- auto size = tinext.tsize; // array element size
-
- foreach (b; arrs)
- length += b.length;
-
- if (!length)
- return null;
-
- auto allocsize = length * size;
- auto info = __arrayAlloc(allocsize, ti, tinext);
- auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
- __setArrayAllocLength(info, allocsize, isshared, tinext);
- void *a = __arrayStart (info);
-
- size_t j = 0;
- foreach (b; arrs)
- {
- if (b.length)
- {
- memcpy(a + j, b.ptr, b.length * size);
- j += b.length * size;
- }
- }
-
- // do postblit processing
- __doPostblit(a, j, tinext);
-
- return a[0..length];
-}
-
-
/**
Allocate an array literal
diff --git a/libphobos/libdruntime/rt/profilegc.d b/libphobos/libdruntime/rt/profilegc.d
index 45e0d51..b97a5c5 100644
--- a/libphobos/libdruntime/rt/profilegc.d
+++ b/libphobos/libdruntime/rt/profilegc.d
@@ -15,6 +15,7 @@ module rt.profilegc;
private:
+import core.stdc.errno;
import core.stdc.stdio;
import core.stdc.stdlib;
import core.stdc.string;
@@ -151,7 +152,7 @@ shared static ~this()
{
qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp);
- FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w");
+ FILE* fp = logfilename == "\0" ? stdout : fopen((logfilename).ptr, "w");
if (fp)
{
fprintf(fp, "bytes allocated, allocations, type, function, file:line\n");
@@ -165,6 +166,12 @@ shared static ~this()
fclose(fp);
}
else
- fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr);
+ {
+ const err = errno;
+ fprintf(stderr, "cannot write profilegc log file '%.*s' (errno=%d)",
+ cast(int) logfilename.length,
+ logfilename.ptr,
+ cast(int) err);
+ }
}
}
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 2b6bc3e..c6e61f7 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
-106038f2eaa70045bf25b29bb1c789304a6065f7
+8ab95ded5265379e74d507fdc252ff3d2305fc26
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d
index 5ecb4f6..5c70960 100644
--- a/libphobos/src/std/algorithm/comparison.d
+++ b/libphobos/src/std/algorithm/comparison.d
@@ -577,19 +577,20 @@ Returns:
and `T3` are different.
*/
T1 clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
-if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val))
- && (is(T2 : T1) && is(T3 : T1)))
-// cannot use :
-// `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1))
-// because of https://issues.dlang.org/show_bug.cgi?id=16235.
-// Once that is fixed, we can simply use the ternary in both the template constraint
-// and the template body
-in
{
+ static assert(is(T2 : T1), "T2 of type '", T2.stringof
+ , "' must be implicitly convertible to type of T1 '"
+ , T1.stringof, "'");
+ static assert(is(T3 : T1), "T3 of type '", T3.stringof
+ , "' must be implicitly convertible to type of T1 '"
+ , T1.stringof, "'");
+
assert(!lower.greaterThan(upper), "Lower can't be greater than upper.");
-}
-do
-{
+
+ // `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1))
+ // because of https://issues.dlang.org/show_bug.cgi?id=16235.
+ // Once that is fixed, we can simply use the ternary in both the template constraint
+ // and the template body
if (val.lessThan(lower))
return lower;
else if (val.greaterThan(upper))
diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d
index 8236076..9f5a6ac 100644
--- a/libphobos/src/std/algorithm/iteration.d
+++ b/libphobos/src/std/algorithm/iteration.d
@@ -3632,18 +3632,18 @@ auto joiner(RoR, Separator)(RoR r, Separator sep)
/// Ditto
auto joiner(RoR)(RoR r)
-if (isInputRange!RoR && isInputRange!(ElementType!RoR))
+if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR)))
{
static struct Result
{
private:
RoR _items;
- ElementType!RoR _current;
+ Unqual!(ElementType!RoR) _current;
enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) &&
isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR);
static if (isBidirectional)
{
- ElementType!RoR _currentBack;
+ Unqual!(ElementType!RoR) _currentBack;
bool reachedFinalElement;
}
@@ -4293,6 +4293,28 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
assert([only(S(null))].joiner.front == S(null));
}
+// https://issues.dlang.org/show_bug.cgi?id=22785
+@safe unittest
+{
+
+ import std.algorithm.iteration : joiner, map;
+ import std.array : array;
+
+ static immutable struct S
+ {
+ int value;
+ }
+
+ static immutable struct T
+ {
+ S[] arr;
+ }
+
+ auto range = [T([S(3)]), T([S(4), S(5)])];
+
+ assert(range.map!"a.arr".joiner.array == [S(3), S(4), S(5)]);
+}
+
/++
Implements the homonym function (also known as `accumulate`, $(D
compress), `inject`, or `foldl`) present in various programming
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
index 15f7ca9..ee318c8 100644
--- a/libphobos/src/std/algorithm/searching.d
+++ b/libphobos/src/std/algorithm/searching.d
@@ -119,12 +119,9 @@ template all(alias pred = "a")
Performs (at most) $(BIGOH range.length) evaluations of `pred`.
+/
bool all(Range)(Range range)
- if (isInputRange!Range)
+ if (isInputRange!Range &&
+ (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front)))))
{
- static assert(is(typeof(unaryFun!pred(range.front))),
- "`" ~ (isSomeString!(typeof(pred))
- ? pred.stringof[1..$-1] : pred.stringof)
- ~ "` isn't a unary predicate function for range.front");
import std.functional : not;
return find!(not!(unaryFun!pred))(range).empty;
@@ -172,7 +169,8 @@ template any(alias pred = "a")
Performs (at most) $(BIGOH range.length) evaluations of `pred`.
+/
bool any(Range)(Range range)
- if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
+ if (isInputRange!Range &&
+ (__traits(isTemplate, pred) || is(typeof(unaryFun!pred(range.front)))))
{
return !find!pred(range).empty;
}
@@ -1294,17 +1292,6 @@ if (isInputRange!R &&
private enum bool hasConstEmptyMember(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool);
-// Rebindable doesn't work with structs
-// see: https://github.com/dlang/phobos/pull/6136
-private template RebindableOrUnqual(T)
-{
- import std.typecons : Rebindable;
- static if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
- alias RebindableOrUnqual = Rebindable!T;
- else
- alias RebindableOrUnqual = Unqual!T;
-}
-
/**
Iterates the passed range and selects the extreme element with `less`.
If the extreme element occurs multiple time, the first occurrence will be
@@ -1313,8 +1300,8 @@ returned.
Params:
map = custom accessor for the comparison key
selector = custom mapping for the extrema selection
- seed = custom seed to use as initial element
r = Range from which the extreme value will be selected
+ seedElement = custom seed to use as initial element
Returns:
The extreme value according to `map` and `selector` of the passed-in values.
@@ -1328,10 +1315,19 @@ in
}
do
{
+ import std.typecons : Rebindable;
+
alias Element = ElementType!Range;
- RebindableOrUnqual!Element seed = r.front;
+ Rebindable!Element seed = r.front;
r.popFront();
- return extremum!(map, selector)(r, seed);
+ static if (is(Rebindable!Element == T[], T))
+ {
+ return extremum!(map, selector)(r, seed);
+ }
+ else
+ {
+ return extremum!(map, selector)(r, seed.get);
+ }
}
private auto extremum(alias map, alias selector = "a < b", Range,
@@ -1341,13 +1337,14 @@ if (isInputRange!Range && !isInfinite!Range &&
!is(CommonType!(ElementType!Range, RangeElementType) == void) &&
is(typeof(unaryFun!map(ElementType!(Range).init))))
{
+ import std.typecons : Rebindable;
+
alias mapFun = unaryFun!map;
alias selectorFun = binaryFun!selector;
alias Element = ElementType!Range;
alias CommonElement = CommonType!(Element, RangeElementType);
- RebindableOrUnqual!CommonElement extremeElement = seedElement;
-
+ Rebindable!CommonElement extremeElement = seedElement;
// if we only have one statement in the loop, it can be optimized a lot better
static if (__traits(isSame, map, a => a))
@@ -1408,7 +1405,15 @@ if (isInputRange!Range && !isInfinite!Range &&
}
}
}
- return extremeElement;
+ // Rebindable is an alias to T for arrays
+ static if (is(typeof(extremeElement) == T[], T))
+ {
+ return extremeElement;
+ }
+ else
+ {
+ return extremeElement.get;
+ }
}
private auto extremum(alias selector = "a < b", Range)(Range r)
@@ -1493,6 +1498,10 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(d.extremum!`a > b` == 10);
assert(d.extremum!(a => a, `a > b`) == 10);
}
+
+ // compiletime
+ enum ctExtremum = iota(1, 5).extremum;
+ assert(ctExtremum == 1);
}
@nogc @safe nothrow pure unittest
@@ -1524,6 +1533,17 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(arr.extremum!"a.val".val == 0);
}
+// https://issues.dlang.org/show_bug.cgi?id=22786
+@nogc @safe nothrow pure unittest
+{
+ struct S
+ {
+ immutable int value;
+ }
+
+ assert([S(5), S(6)].extremum!"a.value" == S(5));
+}
+
// find
/**
Finds an individual element in an $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
@@ -1552,7 +1572,7 @@ Complexity:
`find` performs $(BIGOH walkLength(haystack)) evaluations of `pred`.
There are specializations that improve performance by taking
advantage of $(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives)
- or $(REF_ALTTEXT random access, isRandomAccess, std,range,primitives)
+ or $(REF_ALTTEXT random access, isRandomAccessRange, std,range,primitives)
ranges (where possible).
Params:
diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d
index 4584dcc..2652803 100644
--- a/libphobos/src/std/array.d
+++ b/libphobos/src/std/array.d
@@ -2565,33 +2565,8 @@ E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to)
if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
is(Unqual!E : Unqual!R1))
{
- import std.algorithm.searching : find;
- import std.range : dropOne;
-
- static if (isInputRange!R1)
- {
- if (from.empty) return subject;
- alias rSave = a => a.save;
- }
- else
- {
- alias rSave = a => a;
- }
-
- auto balance = find(subject, rSave(from));
- if (balance.empty)
- return subject;
-
- auto app = appender!(E[])();
- app.put(subject[0 .. subject.length - balance.length]);
- app.put(rSave(to));
- // replacing an element in an array is different to a range replacement
- static if (is(Unqual!E : Unqual!R1))
- replaceInto(app, balance.dropOne, from, to);
- else
- replaceInto(app, balance[from.length .. $], from, to);
-
- return app.data;
+ size_t changed = 0;
+ return replace(subject, from, to, changed);
}
///
@@ -2645,55 +2620,87 @@ if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)
}
/++
- Replace occurrences of `from` with `to` in `subject` and output the result into
- `sink`.
+ Replace occurrences of `from` with `to` in `subject` in a new array.
+ `changed` counts how many replacements took place.
Params:
- sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
subject = the array to scan
from = the item to replace
to = the item to replace all instances of `from` with
+ changed = the number of replacements
- See_Also:
- $(REF substitute, std,algorithm,iteration) for a lazy replace.
+ Returns:
+ A new array without changing the contents of `subject`, or the original
+ array if no match is found.
+/
-void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to)
-if (isOutputRange!(Sink, E) &&
- ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
- is(Unqual!E : Unqual!R1)))
+E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to, ref size_t changed)
+if ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+ is(Unqual!E : Unqual!R1))
{
import std.algorithm.searching : find;
import std.range : dropOne;
static if (isInputRange!R1)
{
- if (from.empty)
- {
- sink.put(subject);
- return;
- }
+ if (from.empty) return subject;
alias rSave = a => a.save;
}
else
{
alias rSave = a => a;
}
- for (;;)
- {
- auto balance = find(subject, rSave(from));
- if (balance.empty)
- {
- sink.put(subject);
- break;
- }
- sink.put(subject[0 .. subject.length - balance.length]);
- sink.put(rSave(to));
- // replacing an element in an array is different to a range replacement
- static if (is(Unqual!E : Unqual!R1))
- subject = balance.dropOne;
- else
- subject = balance[from.length .. $];
- }
+
+ auto balance = find(subject, rSave(from));
+ if (balance.empty)
+ return subject;
+
+ auto app = appender!(E[])();
+ app.put(subject[0 .. subject.length - balance.length]);
+ app.put(rSave(to));
+ ++changed;
+ // replacing an element in an array is different to a range replacement
+ static if (is(Unqual!E : Unqual!R1))
+ replaceInto(app, balance.dropOne, from, to, changed);
+ else
+ replaceInto(app, balance[from.length .. $], from, to, changed);
+
+ return app.data;
+}
+
+///
+@safe unittest
+{
+ size_t changed = 0;
+ assert("Hello Wörld".replace("o Wö", "o Wo", changed) == "Hello World");
+ assert(changed == 1);
+
+ changed = 0;
+ assert("Hello Wörld".replace("l", "h", changed) == "Hehho Wörhd");
+ import std.stdio : writeln;
+ writeln(changed);
+ assert(changed == 3);
+}
+
+/++
+ Replace occurrences of `from` with `to` in `subject` and output the result into
+ `sink`.
+
+ Params:
+ sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
+ subject = the array to scan
+ from = the item to replace
+ to = the item to replace all instances of `from` with
+
+ See_Also:
+ $(REF substitute, std,algorithm,iteration) for a lazy replace.
+ +/
+void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to)
+if (isOutputRange!(Sink, E) &&
+ ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+ is(Unqual!E : Unqual!R1)))
+{
+ size_t changed = 0;
+ replaceInto(sink, subject, from, to, changed);
}
///
@@ -2784,6 +2791,72 @@ if (isOutputRange!(Sink, E) &&
}
/++
+ Replace occurrences of `from` with `to` in `subject` and output the result into
+ `sink`. `changed` counts how many replacements took place.
+
+ Params:
+ sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
+ subject = the array to scan
+ from = the item to replace
+ to = the item to replace all instances of `from` with
+ changed = the number of replacements
+ +/
+void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to, ref size_t changed)
+if (isOutputRange!(Sink, E) &&
+ ((isForwardRange!R1 && isForwardRange!R2 && (hasLength!R2 || isSomeString!R2)) ||
+ is(Unqual!E : Unqual!R1)))
+{
+ import std.algorithm.searching : find;
+ import std.range : dropOne;
+
+ static if (isInputRange!R1)
+ {
+ if (from.empty)
+ {
+ sink.put(subject);
+ return;
+ }
+ alias rSave = a => a.save;
+ }
+ else
+ {
+ alias rSave = a => a;
+ }
+ for (;;)
+ {
+ auto balance = find(subject, rSave(from));
+ if (balance.empty)
+ {
+ sink.put(subject);
+ break;
+ }
+ sink.put(subject[0 .. subject.length - balance.length]);
+ sink.put(rSave(to));
+ ++changed;
+ // replacing an element in an array is different to a range replacement
+ static if (is(Unqual!E : Unqual!R1))
+ subject = balance.dropOne;
+ else
+ subject = balance[from.length .. $];
+ }
+}
+
+///
+@safe unittest
+{
+ auto arr = [1, 2, 3, 4, 5];
+ auto from = [2, 3];
+ auto to = [4, 6];
+ auto sink = appender!(int[])();
+
+ size_t changed = 0;
+ replaceInto(sink, arr, from, to, changed);
+
+ assert(sink.data == [1, 4, 6, 4, 5]);
+ assert(changed == 1);
+}
+
+/++
Replaces elements from `array` with indices ranging from `from`
(inclusive) to `to` (exclusive) with the range `stuff`.
diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d
index 7fe52ba..267f682 100644
--- a/libphobos/src/std/concurrency.d
+++ b/libphobos/src/std/concurrency.d
@@ -2270,7 +2270,9 @@ private
if (range.front.convertsTo!(Throwable))
throw range.front.get!(Throwable);
else if (range.front.convertsTo!(shared(Throwable)))
- throw range.front.get!(shared(Throwable));
+ /* Note: a shared type can be caught without the shared qualifier
+ * so throwing shared will be an error */
+ throw cast() range.front.get!(shared(Throwable));
else
throw new PriorityMessageException(range.front.data);
}
diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d
index 11e3883..4fdf13d 100644
--- a/libphobos/src/std/container/dlist.d
+++ b/libphobos/src/std/container/dlist.d
@@ -198,8 +198,10 @@ struct DList(T)
this (BaseNode _base, T _payload)
{
+ import std.algorithm.mutation : move;
+
this._base = _base;
- this._payload = _payload;
+ this._payload = move(_payload);
}
inout(BaseNode)* asBaseNode() inout @trusted
@@ -216,7 +218,9 @@ struct DList(T)
//Construct as new PayNode, and returns it as a BaseNode.
static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null)
{
- return (new PayNode(BaseNode(prev, next), arg)).asBaseNode();
+ import std.algorithm.mutation : move;
+
+ return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode();
}
void initialize() nothrow @safe pure
@@ -721,7 +725,9 @@ Complexity: $(BIGOH n)
*/
bool linearRemoveElement(T value)
{
- auto n1 = findNodeByValue(_root, value);
+ import std.algorithm.mutation : move;
+
+ auto n1 = findNodeByValue(_root, move(value));
if (n1)
{
auto n2 = n1._next._next;
@@ -1118,3 +1124,27 @@ private:
a.insertFront(iota(0, 5)); // can insert range with non-ref front
assert(a.front == 0 && a.back == 4);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=22147
+@safe unittest
+{
+ import std.algorithm.mutation : move;
+
+ static struct Item
+ {
+ @disable this(this);
+
+ int x;
+ }
+
+ auto list = DList!Item();
+ list.insertFront(Item(1));
+ assert(list[].walkLength == 1);
+ assert(list.front.x == 1);
+ auto item = list.moveFront;
+ item.x = 2;
+ list.front = move(item);
+ assert(list.front.x == 2);
+ list.removeFront();
+ assert(list[].walkLength == 0);
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/region.d b/libphobos/src/std/experimental/allocator/building_blocks/region.d
index 8c39784..a23746a 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/region.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/region.d
@@ -695,25 +695,15 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
import std.conv : to;
import std.traits : hasMember;
import std.typecons : Ternary;
+ import core.thread.types : isStackGrowingDown;
static assert(minAlign.isGoodStaticAlignment);
static assert(size >= minAlign);
- version (X86) enum growDownwards = Yes.growDownwards;
- else version (X86_64) enum growDownwards = Yes.growDownwards;
- else version (ARM) enum growDownwards = Yes.growDownwards;
- else version (AArch64) enum growDownwards = Yes.growDownwards;
- else version (HPPA) enum growDownwards = No.growDownwards;
- else version (PPC) enum growDownwards = Yes.growDownwards;
- else version (PPC64) enum growDownwards = Yes.growDownwards;
- else version (RISCV32) enum growDownwards = Yes.growDownwards;
- else version (RISCV64) enum growDownwards = Yes.growDownwards;
- else version (MIPS32) enum growDownwards = Yes.growDownwards;
- else version (MIPS64) enum growDownwards = Yes.growDownwards;
- else version (SPARC) enum growDownwards = Yes.growDownwards;
- else version (SPARC64) enum growDownwards = Yes.growDownwards;
- else version (SystemZ) enum growDownwards = Yes.growDownwards;
- else static assert(0, "Dunno how the stack grows on this architecture.");
+ static if (isStackGrowingDown)
+ enum growDownwards = Yes.growDownwards;
+ else
+ enum growDownwards = No.growDownwards;
@disable this(this);
diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d
index b7bd3fc..5b8925d 100644
--- a/libphobos/src/std/file.d
+++ b/libphobos/src/std/file.d
@@ -310,6 +310,8 @@ Params:
Returns: Untyped array of bytes _read.
Throws: $(LREF FileException) on error.
+
+See_Also: $(REF readText, std,file) for reading and validating a text file.
*/
void[] read(R)(R name, size_t upTo = size_t.max)
@@ -497,6 +499,8 @@ version (linux) @safe unittest
Throws: $(LREF FileException) if there is an error reading the file,
$(REF UTFException, std, utf) on UTF decoding error.
+
+ See_Also: $(REF read, std,file) for reading a binary file.
+/
S readText(S = string, R)(auto ref R name)
if (isSomeString!S && (isSomeFiniteCharInputRange!R || is(StringTypeOf!R)))
diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d
index 088e77f..219af71 100644
--- a/libphobos/src/std/json.d
+++ b/libphobos/src/std/json.d
@@ -1,7 +1,10 @@
// Written in the D programming language.
/**
-JavaScript Object Notation
+Implements functionality to read and write JavaScript Object Notation values.
+
+JavaScript Object Notation is a lightweight data interchange format commonly used in web services and configuration files.
+It's easy for humans to read and write, and it's easy for machines to parse and generate.
Copyright: Copyright Jeremie Pelletier 2008 - 2009.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -602,6 +605,45 @@ struct JSONValue
assert(j.type == JSONType.object);
}
+ /**
+ * An enum value that can be used to obtain a `JSONValue` representing
+ * an empty JSON object.
+ */
+ enum emptyObject = JSONValue(string[string].init);
+ ///
+ @system unittest
+ {
+ JSONValue obj1 = JSONValue.emptyObject;
+ assert(obj1.type == JSONType.object);
+ obj1.object["a"] = JSONValue(1);
+ assert(obj1.object["a"] == JSONValue(1));
+
+ JSONValue obj2 = JSONValue.emptyObject;
+ assert("a" !in obj2.object);
+ obj2.object["b"] = JSONValue(5);
+ assert(obj1 != obj2);
+ }
+
+ /**
+ * An enum value that can be used to obtain a `JSONValue` representing
+ * an empty JSON array.
+ */
+ enum emptyArray = JSONValue(JSONValue[].init);
+ ///
+ @system unittest
+ {
+ JSONValue arr1 = JSONValue.emptyArray;
+ assert(arr1.type == JSONType.array);
+ assert(arr1.array.length == 0);
+ arr1.array ~= JSONValue("Hello");
+ assert(arr1.array.length == 1);
+ assert(arr1.array[0] == JSONValue("Hello"));
+
+ JSONValue arr2 = JSONValue.emptyArray;
+ assert(arr2.array.length == 0);
+ assert(arr1 != arr2);
+ }
+
void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue))
{
assign(arg);
diff --git a/libphobos/src/std/net/curl.d b/libphobos/src/std/net/curl.d
index 42a34b9..2fcbf94 100644
--- a/libphobos/src/std/net/curl.d
+++ b/libphobos/src/std/net/curl.d
@@ -23,7 +23,7 @@ SMTP) )
)
Note:
-You may need to link to the $(B curl) library, e.g. by adding $(D "libs": ["curl"])
+You may need to link with the $(B curl) library, e.g. by adding $(D "libs": ["curl"])
to your $(B dub.json) file if you are using $(LINK2 http://code.dlang.org, DUB).
Windows x86 note:
@@ -32,20 +32,19 @@ $(LINK2 https://downloads.dlang.org/other/index.html, download archive page).
This module is not available for iOS, tvOS or watchOS.
-Compared to using libcurl directly this module allows simpler client code for
+Compared to using libcurl directly, this module allows simpler client code for
common uses, requires no unsafe operations, and integrates better with the rest
-of the language. Futhermore it provides $(MREF_ALTTEXT range, std,range)
+of the language. Furthermore it provides $(MREF_ALTTEXT range, std,range)
access to protocols supported by libcurl both synchronously and asynchronously.
A high level and a low level API are available. The high level API is built
entirely on top of the low level one.
The high level API is for commonly used functionality such as HTTP/FTP get. The
-$(LREF byLineAsync) and $(LREF byChunkAsync) provides asynchronous
-$(MREF_ALTTEXT range, std,range) that performs the request in another
-thread while handling a line/chunk in the current thread.
+$(LREF byLineAsync) and $(LREF byChunkAsync) functions asynchronously
+perform the request given, outputting the fetched content into a $(MREF_ALTTEXT range, std,range).
-The low level API allows for streaming and other advanced features.
+The low level API allows for streaming, setting request headers and cookies, and other advanced features.
$(BOOKTABLE Cheat Sheet,
$(TR $(TH Function Name) $(TH Description)
@@ -79,18 +78,18 @@ byChunk("dlang.org", 10)) returns a range of ubyte[10] containing the
dlang.org web page.)
)
$(TR $(TDNW $(LREF byLineAsync)) $(TD $(D
-byLineAsync("dlang.org")) returns a range of char[] containing the dlang.org web
- page asynchronously.)
+byLineAsync("dlang.org")) asynchronously returns a range of char[] containing the dlang.org web
+ page.)
)
$(TR $(TDNW $(LREF byChunkAsync)) $(TD $(D
-byChunkAsync("dlang.org", 10)) returns a range of ubyte[10] containing the
-dlang.org web page asynchronously.)
+byChunkAsync("dlang.org", 10)) asynchronously returns a range of ubyte[10] containing the
+dlang.org web page.)
)
$(LEADINGROW Low level
)
-$(TR $(TDNW $(LREF HTTP)) $(TD `HTTP` struct for advanced usage))
-$(TR $(TDNW $(LREF FTP)) $(TD `FTP` struct for advanced usage))
-$(TR $(TDNW $(LREF SMTP)) $(TD `SMTP` struct for advanced usage))
+$(TR $(TDNW $(LREF HTTP)) $(TD Struct for advanced HTTP usage))
+$(TR $(TDNW $(LREF FTP)) $(TD Struct for advanced FTP usage))
+$(TR $(TDNW $(LREF SMTP)) $(TD Struct for advanced SMTP usage))
)
@@ -135,10 +134,10 @@ http.perform();
First, an instance of the reference-counted HTTP struct is created. Then the
custom delegates are set. These will be called whenever the HTTP instance
receives a header and a data buffer, respectively. In this simple example, the
-headers are written to stdout and the data is ignored. If the request should be
+headers are written to stdout and the data is ignored. If the request is
stopped before it has finished then return something less than data.length from
the onReceive callback. See $(LREF onReceiveHeader)/$(LREF onReceive) for more
-information. Finally the HTTP request is effected by calling perform(), which is
+information. Finally, the HTTP request is performed by calling perform(), which is
synchronous.
Source: $(PHOBOSSRC std/net/curl.d)
@@ -147,8 +146,8 @@ Copyright: Copyright Jonas Drewsen 2011-2012
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Jonas Drewsen. Some of the SMTP code contributed by Jimmy Cao.
-Credits: The functionally is based on $(HTTP curl.haxx.se/libcurl, libcurl).
- LibCurl is licensed under an MIT/X derivative license.
+Credits: The functionality is based on $(HTTP curl.haxx.se/libcurl, libcurl).
+ libcurl is licensed under an MIT/X derivative license.
*/
/*
Copyright Jonas Drewsen 2011 - 2012.
diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d
index 4b5a7ef..63d60d1 100644
--- a/libphobos/src/std/path.d
+++ b/libphobos/src/std/path.d
@@ -1758,7 +1758,6 @@ immutable(C)[] buildNormalizedPath(C)(const(C[])[] paths...)
if (isSomeChar!C)
{
import std.array : array;
- import std.exception : assumeUnique;
const(C)[] chained;
foreach (path; paths)
@@ -1770,7 +1769,7 @@ if (isSomeChar!C)
}
auto result = asNormalizedPath(chained);
// .array returns a copy, so it is unique
- return () @trusted { return assumeUnique(result.array); } ();
+ return result.array;
}
///
diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d
index 93be764..066ed17 100644
--- a/libphobos/src/std/random.d
+++ b/libphobos/src/std/random.d
@@ -812,7 +812,7 @@ Parameters for the generator.
// Bitmasks used in the 'twist' part of the algorithm
private enum UIntType lowerMask = (cast(UIntType) 1u << r) - 1,
- upperMask = (~lowerMask) & this.max;
+ upperMask = (~lowerMask) & max;
/*
Collection of all state variables
@@ -905,17 +905,17 @@ Parameters for the generator.
private static void seedImpl(UIntType value, ref State mtState) @nogc
{
mtState.data[$ - 1] = value;
- static if (this.max != UIntType.max)
+ static if (max != UIntType.max)
{
- mtState.data[$ - 1] &= this.max;
+ mtState.data[$ - 1] &= max;
}
foreach_reverse (size_t i, ref e; mtState.data[0 .. $ - 1])
{
e = f * (mtState.data[i + 1] ^ (mtState.data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1));
- static if (this.max != UIntType.max)
+ static if (max != UIntType.max)
{
- e &= this.max;
+ e &= max;
}
}
diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d
index 888ac1a..7a724b0 100644
--- a/libphobos/src/std/range/package.d
+++ b/libphobos/src/std/range/package.d
@@ -235,7 +235,7 @@ public import std.range.primitives;
public import std.typecons : Flag, Yes, No;
import std.internal.attributes : betterC;
-import std.meta : allSatisfy, anySatisfy, staticMap;
+import std.meta : aliasSeqOf, allSatisfy, anySatisfy, staticMap;
import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral,
isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf;
@@ -313,12 +313,16 @@ if (isBidirectionalRange!(Unqual!Range))
{
@property void front(ElementType!R val)
{
- source.back = val;
+ import std.algorithm.mutation : move;
+
+ source.back = move(val);
}
@property void back(ElementType!R val)
{
- source.front = val;
+ import std.algorithm.mutation : move;
+
+ source.front = move(val);
}
}
@@ -330,7 +334,9 @@ if (isBidirectionalRange!(Unqual!Range))
{
void opIndexAssign(ElementType!R val, size_t n)
{
- source[retroIndex(n)] = val;
+ import std.algorithm.mutation : move;
+
+ source[retroIndex(n)] = move(val);
}
}
@@ -474,6 +480,19 @@ pure @safe nothrow @nogc unittest
foreach (x; data.retro) {}
}
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static struct S {
+ int v;
+ @disable this(this);
+ }
+
+ immutable foo = [S(1), S(2), S(3)];
+ auto r = retro(foo);
+ assert(equal(r, [S(3), S(2), S(1)]));
+}
/**
Iterates range `r` with stride `n`. If the range is a
@@ -585,7 +604,9 @@ do
{
@property void front(ElementType!R val)
{
- source.front = val;
+ import std.algorithm.mutation : move;
+
+ source.front = move(val);
}
}
@@ -864,6 +885,20 @@ pure @safe nothrow unittest
assert(equal(s, [1L, 4L, 7L]));
}
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static struct S {
+ int v;
+ @disable this(this);
+ }
+
+ immutable foo = [S(1), S(2), S(3), S(4), S(5)];
+ auto r = stride(foo, 3);
+ assert(equal(r, [S(1), S(4)]));
+}
+
/**
Spans multiple ranges in sequence. The function `chain` takes any
number of ranges and returns a $(D Chain!(R1, R2,...)) object. The
@@ -872,6 +907,11 @@ result is a range that offers the `front`, `popFront`, and $(D
empty) primitives. If all input ranges offer random access and $(D
length), `Chain` offers them as well.
+Note that repeated random access of the resulting range is likely
+to perform somewhat badly since lengths of the ranges in the chain have to be
+added up for each random access operation. Random access to elements of
+the first remaining range is still efficient.
+
If only one range is offered to `Chain` or `chain`, the $(D
Chain) type exits the picture by aliasing itself directly to that
range's type.
@@ -907,7 +947,12 @@ if (Ranges.length > 0 &&
enum sameET = is(.ElementType!A == RvalueElementType);
}
- enum bool allSameType = allSatisfy!(sameET, R);
+ enum bool allSameType = allSatisfy!(sameET, R),
+ bidirectional = allSatisfy!(isBidirectionalRange, R),
+ mobileElements = allSatisfy!(hasMobileElements, R),
+ assignableElements = allSameType
+ && allSatisfy!(hasAssignableElements, R);
+
alias ElementType = RvalueElementType;
static if (allSameType && allSatisfy!(hasLvalueElements, R))
@@ -925,17 +970,44 @@ if (Ranges.length > 0 &&
}
}
- // This is the entire state
R source;
- // TODO: use a vtable (or more) instead of linear iteration
+ size_t frontIndex;
+ // Always points to index one past the last non-empty range,
+ // because otherwise decrementing while pointing to first range
+ // would overflow to size_t.max.
+ static if (bidirectional) size_t backIndex;
+ else enum backIndex = source.length;
public:
this(R input)
{
- // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209
- static foreach (i, v; input)
+ frontIndex = source.length;
+ static if (bidirectional) backIndex = 0;
+
+ foreach (i, ref v; input) source[i] = v;
+
+ // We do this separately to avoid invoking `empty` needlessly.
+ // While not recommended, a range may depend on side effects of
+ // `empty` call.
+ foreach (i, ref v; input) if (!v.empty)
{
- source[i] = v;
+ frontIndex = i;
+ static if (bidirectional) backIndex = i+1;
+ break;
+ }
+
+ // backIndex is already set in the first loop to
+ // as frontIndex+1, so we'll use that if we don't find a
+ // non-empty range here.
+ static if (bidirectional)
+ static foreach_reverse (i; 1 .. R.length + 1)
+ {
+ if (i <= frontIndex + 1) return;
+ if (!input[i-1].empty)
+ {
+ backIndex = i;
+ return;
+ }
}
}
@@ -948,120 +1020,209 @@ if (Ranges.length > 0 &&
}
else
{
- @property bool empty()
+ @property bool empty() => frontIndex >= backIndex;
+ }
+
+ static if (allSatisfy!(isForwardRange, R))
+ {
+ @property auto save()
{
- foreach (i, Unused; R)
+ auto saveI(size_t i)() => source[i].save;
+
+ // TODO: this has the constructor needlessly refind
+ // frontIndex and backIndex. It'd be better to just copy
+ // those from `.this`.
+ auto saveResult =
+ Result(staticMap!(saveI, aliasSeqOf!(R.length.iota)));
+
+ return saveResult;
+ }
+ }
+
+ void popFront()
+ {
+ sw1: switch (frontIndex)
+ {
+ static foreach (i; 0 .. R.length)
{
- if (!source[i].empty) return false;
+ case i:
+ source[i].popFront();
+ break sw1;
}
- return true;
+
+ case R.length:
+ assert(0, "Attempt to `popFront` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- }
- static if (allSatisfy!(isForwardRange, R))
- @property auto save()
+ sw2: switch (frontIndex)
{
- auto saveSource(size_t len)()
+ static foreach (i; 0 .. R.length)
{
- import std.typecons : tuple;
- static assert(len > 0);
- static if (len == 1)
- {
- return tuple(source[0].save);
- }
- else
+ case i:
+ if (source[i].empty)
{
- return saveSource!(len - 1)() ~
- tuple(source[len - 1].save);
+ frontIndex++;
+ goto case;
}
+ else break sw2;
}
- return Result(saveSource!(R.length).expand);
- }
- void popFront()
- {
- foreach (i, Unused; R)
- {
- if (source[i].empty) continue;
- source[i].popFront();
- return;
+ // Only possible to reach from goto of previous case.
+ case R.length:
+ break;
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to `popFront` of empty `chain` range");
}
@property auto ref front()
{
- foreach (i, Unused; R)
+ switch (frontIndex)
{
- if (source[i].empty) continue;
- return fixRef(source[i].front);
+ static foreach (i; 0 .. R.length)
+ {
+ case i:
+ return fixRef(source[i].front);
+ }
+
+ case R.length:
+ assert(0, "Attempt to get `front` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to get `front` of empty `chain` range");
}
- static if (allSameType && allSatisfy!(hasAssignableElements, R))
+ static if (assignableElements)
{
// @@@BUG@@@
//@property void front(T)(T v) if (is(T : RvalueElementType))
@property void front(RvalueElementType v)
{
- foreach (i, Unused; R)
+ import std.algorithm.mutation : move;
+
+ sw: switch (frontIndex)
{
- if (source[i].empty) continue;
- source[i].front = v;
- return;
+ static foreach (i; 0 .. R.length)
+ {
+ case i:
+ source[i].front = move(v);
+ break sw;
+ }
+
+ case R.length:
+ assert(0, "Attempt to set `front` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to set `front` of empty `chain` range");
}
}
- static if (allSatisfy!(hasMobileElements, R))
+ static if (mobileElements)
{
RvalueElementType moveFront()
{
- foreach (i, Unused; R)
+ switch (frontIndex)
{
- if (source[i].empty) continue;
- return source[i].moveFront();
+ static foreach (i; 0 .. R.length)
+ {
+ case i:
+ return source[i].moveFront();
+ }
+
+ case R.length:
+ assert(0, "Attempt to `moveFront` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to `moveFront` of empty `chain` range");
}
}
- static if (allSatisfy!(isBidirectionalRange, R))
+ static if (bidirectional)
{
@property auto ref back()
{
- foreach_reverse (i, Unused; R)
+ switch (backIndex)
{
- if (source[i].empty) continue;
- return fixRef(source[i].back);
+ static foreach_reverse (i; 1 .. R.length + 1)
+ {
+ case i:
+ return fixRef(source[i-1].back);
+ }
+
+ case 0:
+ assert(0, "Attempt to get `back` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to get `back` of empty `chain` range");
}
void popBack()
{
- foreach_reverse (i, Unused; R)
+ sw1: switch (backIndex)
{
- if (source[i].empty) continue;
- source[i].popBack();
- return;
+ static foreach_reverse (i; 1 .. R.length + 1)
+ {
+ case i:
+ source[i-1].popBack();
+ break sw1;
+ }
+
+ case 0:
+ assert(0, "Attempt to `popFront` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
+ }
+
+ sw2: switch (backIndex)
+ {
+ static foreach_reverse (i; 1 .. R.length + 1)
+ {
+ case i:
+ if (source[i-1].empty)
+ {
+ backIndex--;
+ goto case;
+ }
+ else break sw2;
+ }
+
+ // Only possible to reach from goto of previous case.
+ case 0:
+ break;
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to `popBack` of empty `chain` range");
}
- static if (allSatisfy!(hasMobileElements, R))
+ static if (mobileElements)
{
RvalueElementType moveBack()
{
- foreach_reverse (i, Unused; R)
+ switch (backIndex)
{
- if (source[i].empty) continue;
- return source[i].moveBack();
+ static foreach_reverse (i; 1 .. R.length + 1)
+ {
+ case i:
+ return source[i-1].moveBack();
+ }
+
+ case 0:
+ assert(0, "Attempt to `moveBack` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to `moveBack` of empty `chain` range");
}
}
@@ -1069,13 +1230,23 @@ if (Ranges.length > 0 &&
{
@property void back(RvalueElementType v)
{
- foreach_reverse (i, Unused; R)
+ import std.algorithm.mutation : move;
+
+ sw: switch (backIndex)
{
- if (source[i].empty) continue;
- source[i].back = v;
- return;
+ static foreach_reverse (i; 1 .. R.length + 1)
+ {
+ case i:
+ source[i-1].back = move(v);
+ break sw;
+ }
+
+ case 0:
+ assert(0, "Attempt to set `back` of empty `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to set `back` of empty `chain` range");
}
}
}
@@ -1084,11 +1255,24 @@ if (Ranges.length > 0 &&
{
@property size_t length()
{
- size_t result;
- foreach (i, Unused; R)
+ size_t result = 0;
+ sw: switch (frontIndex)
{
- result += source[i].length;
+ static foreach (i; 0 .. R.length)
+ {
+ case i:
+ result += source[i].length;
+ if (backIndex == i+1) break sw;
+ else goto case;
+ }
+
+ case R.length:
+ break;
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
+
return result;
}
@@ -1099,64 +1283,93 @@ if (Ranges.length > 0 &&
{
auto ref opIndex(size_t index)
{
- foreach (i, Range; R)
+ switch (frontIndex)
{
- static if (isInfinite!(Range))
- {
- return source[i][index];
- }
- else
+ static foreach (i; 0 .. R.length)
{
- immutable length = source[i].length;
- if (index < length) return fixRef(source[i][index]);
- index -= length;
+ case i:
+ static if (!isInfinite!(R[i]))
+ {
+ immutable length = source[i].length;
+ if (index >= length)
+ {
+ index -= length;
+ goto case;
+ }
+ }
+
+ return fixRef(source[i][index]);
}
+
+ case R.length:
+ assert(0, "Attempt to access out-of-bounds index of `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to access out-of-bounds index of `chain` range");
}
- static if (allSatisfy!(hasMobileElements, R))
+ static if (mobileElements)
{
RvalueElementType moveAt(size_t index)
{
- foreach (i, Range; R)
+ switch (frontIndex)
{
- static if (isInfinite!(Range))
+ static foreach (i; 0 .. R.length)
{
+ case i:
+ static if (!isInfinite!(R[i]))
+ {
+ immutable length = source[i].length;
+ if (index >= length)
+ {
+ index -= length;
+ goto case;
+ }
+ }
+
return source[i].moveAt(index);
}
- else
- {
- immutable length = source[i].length;
- if (index < length) return source[i].moveAt(index);
- index -= length;
- }
+
+ case R.length:
+ assert(0, "Attempt to move out-of-bounds index of `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to move out-of-bounds index of `chain` range");
}
}
static if (allSameType && allSatisfy!(hasAssignableElements, R))
void opIndexAssign(ElementType v, size_t index)
{
- foreach (i, Range; R)
+ import std.algorithm.mutation : move;
+
+ sw: switch (frontIndex)
{
- static if (isInfinite!(Range))
+ static foreach (i; 0 .. R.length)
{
- source[i][index] = v;
- }
- else
- {
- immutable length = source[i].length;
- if (index < length)
+ case i:
+ static if (!isInfinite!(R[i]))
{
- source[i][index] = v;
- return;
+ immutable length = source[i].length;
+ if (index >= length)
+ {
+ index -= length;
+ goto case;
+ }
}
- index -= length;
+
+ source[i][index] = move(v);
+ break sw;
}
+
+ case R.length:
+ assert(0, "Attempt to write out-of-bounds index of `chain` range");
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- assert(false, "Attempt to write out-of-bounds index of `chain` range");
}
}
@@ -1164,40 +1377,74 @@ if (Ranges.length > 0 &&
auto opSlice(size_t begin, size_t end) return scope
{
auto result = this;
- foreach (i, Unused; R)
+
+ sw: switch (frontIndex)
{
- immutable len = result.source[i].length;
- if (len < begin)
- {
- result.source[i] = result.source[i]
- [len .. len];
- begin -= len;
- }
- else
+ static foreach (i; 0 .. R.length)
{
- result.source[i] = result.source[i]
- [begin .. len];
- break;
+ case i:
+ immutable len = result.source[i].length;
+ if (len <= begin)
+ {
+ result.source[i] = result.source[i]
+ [len .. len];
+ begin -= len;
+ result.frontIndex++;
+ goto case;
+ }
+ else
+ {
+ result.source[i] = result.source[i]
+ [begin .. len];
+ break sw;
+ }
}
+
+ case R.length:
+ assert(begin == 0,
+ "Attempt to access out-of-bounds slice of `chain` range");
+ break;
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
- auto cut = length;
- cut = cut <= end ? 0 : cut - end;
- foreach_reverse (i, Unused; R)
+
+ // Overflow intentional if end index too big.
+ // This will trigger the bounds check failure below.
+ auto cut = length - end;
+
+ sw2: switch (backIndex)
{
- immutable len = result.source[i].length;
- if (cut > len)
+ static foreach_reverse (i; 1 .. R.length + 1)
{
- result.source[i] = result.source[i]
- [0 .. 0];
- cut -= len;
- }
- else
- {
- result.source[i] = result.source[i]
- [0 .. len - cut];
- break;
+ case i:
+ immutable len = result.source[i-1].length;
+ if (len <= cut)
+ {
+ result.source[i-1] = result.source[i-1]
+ [0 .. 0];
+ cut -= len;
+ result.backIndex--;
+ goto case;
+ }
+ else
+ {
+ result.source[i-1] = result.source[i-1]
+ [0 .. len - cut];
+ break sw2;
+ }
}
+
+ case 0:
+ assert(cut == 0, end > length?
+ "Attempt to access out-of-bounds slice of `chain` range":
+ "Attempt to access negative length slice of `chain` range");
+ break sw2;
+
+ default:
+ assert(0, "Internal library error. Please report it.");
}
+
return result;
}
}
@@ -1293,6 +1540,10 @@ pure @safe nothrow unittest
auto s = chain(arr1, arr2, arr3);
assert(s[5] == 6);
assert(equal(s, witness));
+ assert(s[4 .. 6].equal(arr2));
+ assert(s[2 .. 5].equal([3, 4, 5]));
+ assert(s[0 .. 0].empty);
+ assert(s[7 .. $].empty);
assert(s[5] == 6);
}
{
@@ -1392,6 +1643,28 @@ pure @safe unittest
assert(equal(r, "foobar"));
}
+pure @safe nothrow @nogc unittest
+{
+ // support non-copyable items
+
+ static struct S {
+ int v;
+ @disable this(this);
+ }
+
+ S[2] s0, s1;
+ foreach (ref el; chain(s0[], s1[]))
+ {
+ int n = el.v;
+ }
+
+ S[] s2, s3;
+ foreach (ref el; chain(s2, s3))
+ {
+ int n = el.v;
+ }
+}
+
/**
Choose one of two ranges at runtime depending on a Boolean condition.
@@ -1825,6 +2098,23 @@ pure @safe unittest
auto chosen2 = chosen.save;
}
+pure @safe nothrow unittest
+{
+ static struct S {
+ int v;
+ @disable this(this);
+ }
+
+ auto a = [S(1), S(2), S(3)];
+ auto b = [S(4), S(5), S(6)];
+
+ auto chosen = choose(true, a, b);
+ assert(chosen.front.v == 1);
+
+ auto chosen2 = choose(false, a, b);
+ assert(chosen2.front.v == 4);
+}
+
/**
Choose one of multiple ranges at runtime.
@@ -2122,6 +2412,20 @@ pure @safe unittest
assert(equal(r.save, "fboaor"));
assert(equal(r.save, "fboaor"));
}
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static struct S {
+ int v;
+ @disable this(this);
+ }
+
+ S[] a = [ S(1), S(2) ];
+ S[] b = [ S(10), S(20) ];
+ auto r = roundRobin(a, b);
+ assert(equal(r, [ S(1), S(10), S(2), S(20) ]));
+}
/**
Iterates a random-access range starting from a given point and
@@ -2313,12 +2617,12 @@ if (isInputRange!(Unqual!Range) &&
/// ditto
@property void front(ElementType!R v)
{
+ import std.algorithm.mutation : move;
+
assert(!empty,
"Attempting to assign to the front of an empty "
~ Take.stringof);
- // This has to return auto instead of void because of
- // https://issues.dlang.org/show_bug.cgi?id=4706
- source.front = v;
+ source.front = move(v);
}
static if (hasMobileElements!R)
@@ -2694,10 +2998,12 @@ if (isInputRange!R)
{
@property auto ref front(ElementType!R v)
{
+ import std.algorithm.mutation : move;
+
assert(!empty,
"Attempting to assign to the front of an empty "
~ typeof(this).stringof);
- return _input.front = v;
+ return _input.front = move(v);
}
}
}
@@ -3929,7 +4235,9 @@ if (isForwardRange!R && !isInfinite!R)
/// ditto
@property void front(ElementType!R val)
{
- _original[_index] = val;
+ import std.algorithm.mutation : move;
+
+ _original[_index] = move(val);
}
}
@@ -4039,7 +4347,9 @@ if (isForwardRange!R && !isInfinite!R)
/// ditto
@property auto front(ElementType!R val)
{
- return _current.front = val;
+ import std.algorithm.mutation : move;
+
+ return _current.front = move(val);
}
}
@@ -7053,7 +7363,9 @@ struct FrontTransversal(Ror,
{
@property void front(ElementType val)
{
- _input.front.front = val;
+ import std.algorithm.mutation : move;
+
+ _input.front.front = move(val);
}
}
@@ -7110,7 +7422,9 @@ struct FrontTransversal(Ror,
{
@property void back(ElementType val)
{
- _input.back.front = val;
+ import std.algorithm.mutation : move;
+
+ _input.back.front = move(val);
}
}
}
@@ -7143,7 +7457,9 @@ struct FrontTransversal(Ror,
{
void opIndexAssign(ElementType val, size_t n)
{
- _input[n].front = val;
+ import std.algorithm.mutation : move;
+
+ _input[n].front = move(val);
}
}
mixin ImplementLength!_input;
@@ -8785,7 +9101,8 @@ public:
{
// `nextSource` is used to "look one step into the future" and check for the end
// this means `nextSource` is advanced by `stepSize` on every `popFront`
- nextSource = source.save.drop(windowSize);
+ nextSource = source.save;
+ auto poppedElems = nextSource.popFrontN(windowSize);
}
if (source.empty)
@@ -8798,7 +9115,7 @@ public:
{
static if (needsEndTracker)
{
- if (nextSource.empty)
+ if (poppedElems < windowSize)
hasShownPartialBefore = true;
}
else
@@ -8806,14 +9123,13 @@ public:
if (source.length <= windowSize)
hasShownPartialBefore = true;
}
-
}
else
{
// empty source range is needed, s.t. length, slicing etc. works properly
static if (needsEndTracker)
{
- if (nextSource.empty)
+ if (poppedElems < windowSize)
_empty = true;
}
else
@@ -9770,6 +10086,15 @@ public:
assert([1].map!(x => x).slide(2).equal!equal([[1]]));
}
+// https://issues.dlang.org/show_bug.cgi?id=19642
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : splitter;
+
+ assert("ab cd".splitter(' ').slide!(No.withPartial)(2).equal!equal([["ab", "cd"]]));
+}
+
private struct OnlyResult(Values...)
if (Values.length > 1)
{
diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d
index 748fde3..e581409 100644
--- a/libphobos/src/std/range/primitives.d
+++ b/libphobos/src/std/range/primitives.d
@@ -1041,7 +1041,8 @@ See_Also:
*/
enum bool isBidirectionalRange(R) = isForwardRange!R
&& is(typeof((R r) => r.popBack))
- && is(typeof((R r) { return r.back; } (R.init)) == ElementType!R);
+ && (is(typeof((return ref R r) => r.back)) || is(typeof(ref (return ref R r) => r.back)))
+ && is(typeof(R.init.back.init) == ElementType!R);
///
@safe unittest
diff --git a/libphobos/src/std/regex/internal/backtracking.d b/libphobos/src/std/regex/internal/backtracking.d
index 21766fc..ac73f70 100644
--- a/libphobos/src/std/regex/internal/backtracking.d
+++ b/libphobos/src/std/regex/internal/backtracking.d
@@ -73,12 +73,7 @@ final:
override @property ref size_t refCount() { return _refCount; }
override @property ref const(RegEx) pattern(){ return re; }
- static if (__traits(hasMember,Stream, "search"))
- {
- enum kicked = true;
- }
- else
- enum kicked = false;
+ enum kicked = __traits(hasMember, Stream, "search");
static size_t initialMemory(const ref RegEx re)
{
diff --git a/libphobos/src/std/regex/internal/thompson.d b/libphobos/src/std/regex/internal/thompson.d
index ab28d37..f4643ae 100644
--- a/libphobos/src/std/regex/internal/thompson.d
+++ b/libphobos/src/std/regex/internal/thompson.d
@@ -759,12 +759,7 @@ final:
}
- static if (__traits(hasMember,Stream, "search"))
- {
- enum kicked = true;
- }
- else
- enum kicked = false;
+ enum kicked = __traits(hasMember, Stream, "search");
static size_t getThreadSize(const ref Regex!Char re)
{
diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d
index 92e4906..5ed685f 100644
--- a/libphobos/src/std/stdio.d
+++ b/libphobos/src/std/stdio.d
@@ -43,6 +43,8 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright),
$(HTTP erdani.org, Andrei Alexandrescu),
Alex Rønne Petersen
+Macros:
+CSTDIO=$(HTTP cplusplus.com/reference/cstdio/$1/, $1)
*/
module std.stdio;
@@ -534,8 +536,7 @@ Params:
name = range or string representing the file _name
stdioOpenmode = range or string represting the open mode
(with the same semantics as in the C standard library
- $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
- function)
+ $(CSTDIO fopen) function)
Throws: `ErrnoException` if the file could not be opened.
*/
@@ -618,8 +619,7 @@ file.
/**
Detaches from the current file (throwing on failure), and then attempts to
_open file `name` with mode `stdioOpenmode`. The mode has the
-same semantics as in the C standard library $(HTTP
-cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
+same semantics as in the C standard library $(CSTDIO fopen) function.
Throws: `ErrnoException` in case of error.
*/
@@ -735,8 +735,7 @@ Reuses the `File` object to either open a different file, or change
the file mode. If `name` is `null`, the mode of the currently open
file is changed; otherwise, a new file is opened, reusing the C
`FILE*`. The function has the same semantics as in the C standard
-library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen)
-function.
+library $(CSTDIO freopen) function.
Note: Calling `reopen` with a `null` `name` is not implemented
in all C runtimes.
@@ -832,8 +831,8 @@ Throws: `ErrnoException` in case of error.
Params:
fd = File descriptor to associate with this `File`.
stdioOpenmode = Mode to associate with this File. The mode has the same semantics
- semantics as in the C standard library
- $(HTTP cplusplus.com/reference/cstdio/fopen/, fdopen) function, and must be compatible with `fd`.
+ semantics as in the C standard library $(CSTDIO fdopen) function,
+ and must be compatible with `fd`.
*/
void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe
{
@@ -932,8 +931,7 @@ Throws: `ErrnoException` in case of error.
}
/**
-Returns `true` if the file is at end (see $(HTTP
-cplusplus.com/reference/clibrary/cstdio/feof.html, feof)).
+Returns `true` if the file is at end (see $(CSTDIO feof)).
Throws: `Exception` if the file is not opened.
*/
@@ -961,8 +959,7 @@ Throws: `Exception` if the file is not opened.
/**
If the file is closed or not yet opened, returns `true`. Otherwise, returns
-$(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
-the file handle.
+$(CSTDIO ferror) for the file handle.
*/
@property bool error() const @trusted pure nothrow
{
@@ -1017,8 +1014,7 @@ Throws: `ErrnoException` on failure if closing the file.
/**
If the file was closed or not yet opened, succeeds vacuously. Otherwise
-closes the file (by calling $(HTTP
-cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
+closes the file (by calling $(CSTDIO fclose)),
throwing on error. Even if an exception is thrown, afterwards the $(D
File) object is empty. This is different from `detach` in that it
always closes the file; consequently, all other `File` objects
@@ -1046,8 +1042,7 @@ Throws: `ErrnoException` on error.
/**
If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns
-$(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
-_clearerr) for the file handle.
+$(CSTDIO clearerr) for the file handle.
*/
void clearerr() @safe pure nothrow
{
@@ -1058,8 +1053,7 @@ _clearerr) for the file handle.
/**
Flushes the C `FILE` buffers.
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush)
-for the file handle.
+Calls $(CSTDIO fflush) for the file handle.
Throws: `Exception` if the file is not opened or if the call to `fflush` fails.
*/
@@ -1125,7 +1119,7 @@ Throws: `Exception` if the file is not opened or if the OS call fails.
}
/**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
+Calls $(CSTDIO fread) for the
file handle. The number of items to read and the size of
each item is inferred from the size and type of the input array, respectively.
@@ -1220,7 +1214,7 @@ Throws: `ErrnoException` if the file is not opened or the call to `fread` fails.
}
/**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file
+Calls $(CSTDIO fwrite) for the file
handle. The number of items to write and the size of each
item is inferred from the size and type of the input array, respectively. An
error is thrown if the buffer could not be written in its entirety.
@@ -1290,7 +1284,7 @@ Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fa
}
/**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
+Calls $(CSTDIO fseek)
for the file handle to move its position indicator.
Params:
@@ -1374,7 +1368,7 @@ Throws: `Exception` if the file is not opened.
}
/**
-Calls $(HTTP cplusplus.com/reference/cstdio/ftell.html, ftell)
+Calls $(CSTDIO ftell)
for the managed file handle, which returns the current value of
the position indicator of the file handle.
@@ -1420,8 +1414,7 @@ Throws: `Exception` if the file is not opened.
}
/**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
-for the file handle.
+Calls $(CSTDIO rewind) for the file handle.
Throws: `Exception` if the file is not opened.
*/
@@ -1434,8 +1427,7 @@ Throws: `Exception` if the file is not opened.
}
/**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
-the file handle.
+Calls $(CSTDIO setvbuf) for the file handle.
Throws: `Exception` if the file is not opened.
`ErrnoException` if the call to `setvbuf` fails.
@@ -1450,8 +1442,7 @@ Throws: `Exception` if the file is not opened.
}
/**
-Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
-_setvbuf) for the file handle.
+Calls $(CSTDIO setvbuf) for the file handle.
Throws: `Exception` if the file is not opened.
`ErrnoException` if the call to `setvbuf` fails.
@@ -2252,8 +2243,7 @@ $(CONSOLE
}
/**
- Returns a temporary file by calling
- $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile).
+ Returns a temporary file by calling $(CSTDIO tmpfile).
Note that the created file has no $(LREF name).*/
static File tmpfile() @safe
{
diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d
index f4d011bb..689f0ae 100644
--- a/libphobos/src/std/traits.d
+++ b/libphobos/src/std/traits.d
@@ -1198,7 +1198,7 @@ if (isCallable!func)
}
/**
-Convert the result of `__traits(getParameterStorageClasses)`
+Convert the result of $(DDSUBLINK spec/traits, getParameterStorageClasses, `__traits(getParameterStorageClasses)`)
to $(LREF ParameterStorageClass) `enum`s.
Params:
@@ -2184,7 +2184,7 @@ if (isCallable!func)
void func() {}
static assert(variadicFunctionStyle!func == Variadic.no);
- extern(C) int printf(in char*, ...);
+ extern(C) int printf(const char*, ...);
static assert(variadicFunctionStyle!printf == Variadic.c);
}
@@ -2572,6 +2572,8 @@ if (is(T == class))
/**
Determines whether `T` has its own context pointer.
`T` must be either `class`, `struct`, or `union`.
+
+See also: $(DDSUBLINK spec/traits, isNested, `__traits(isNested, T)`)
*/
template isNested(T)
if (is(T == class) || is(T == struct) || is(T == union))
@@ -3857,6 +3859,8 @@ package alias Identity(alias A) = A;
/**
Yields `true` if and only if `T` is an aggregate that defines
a symbol called `name`.
+
+ See also: $(DDSUBLINK spec/traits, hasMember, `__traits(hasMember, T, name)`)
*/
enum hasMember(T, string name) = __traits(hasMember, T, name);
@@ -4839,6 +4843,8 @@ package enum maxAlignment(U...) =
/**
Returns class instance alignment.
+
+See also: $(DDSUBLINK spec/traits, classInstanceAlignment, `__traits(classInstanceAlignment, T)`)
*/
template classInstanceAlignment(T)
if (is(T == class))
@@ -5313,7 +5319,7 @@ enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lv
}
else
{
- mixin(q{ struct S6 { void opAssign(in ref S5); } });
+ mixin(q{ struct S6 { void opAssign(scope const ref S5); } });
static assert(!isRvalueAssignable!(S6, S5));
static assert( isLvalueAssignable!(S6, S5));
@@ -5680,7 +5686,7 @@ private struct __InoutWorkaroundStruct{}
/**
Creates an lvalue or rvalue of type `T` for `typeof(...)` and
-`__traits(compiles, ...)` purposes. No actual value is returned.
+$(DDSUBLINK spec/traits, compiles, `__traits(compiles, ...)`) purposes. No actual value is returned.
Params:
T = The type to transform
@@ -6166,7 +6172,7 @@ template BuiltinTypeOf(T)
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/**
- * Detect whether `T` is a built-in boolean type.
+ * Detect whether `T` is a built-in boolean type or enum of boolean base type.
*/
enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool);
@@ -6196,8 +6202,15 @@ enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool);
}
/**
- * Detect whether `T` is a built-in integral type. Types `bool`,
- * `char`, `wchar`, and `dchar` are not considered integral.
+ * Detect whether `T` is a built-in integral type.
+ * Integral types are `byte`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `cent`, `ucent`,
+ * and enums with an integral type as its base type.
+ * Params:
+ * T = type to test
+ * Returns:
+ * `true` if `T` is an integral type
+ * Note:
+ * this is not the same as $(LINK2 https://dlang.org/spec/traits.html#isIntegral, `__traits(isIntegral)`)
*/
template isIntegral(T)
{
@@ -6260,7 +6273,10 @@ template isIntegral(T)
/**
* Detect whether `T` is a built-in floating point type.
+ *
+ * See also: $(DDSUBLINK spec/traits, isFloating, `__traits(isFloating, T)`)
*/
+// is(T : real) to discount complex types
enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real);
///
@@ -6398,7 +6414,10 @@ template isNumeric(T)
/**
* Detect whether `T` is a scalar type (a built-in numeric, character or
* boolean type).
+ *
+ * See also: $(DDSUBLINK spec/traits, isScalar, `__traits(isScalar, T)`)
*/
+// is(T : real) to discount complex types
enum bool isScalarType(T) = __traits(isScalar, T) && is(T : real);
///
@@ -6927,6 +6946,8 @@ template isAutodecodableString(T)
/**
* Detect whether type `T` is a static array.
+ *
+ * See also: $(DDSUBLINK spec/traits, isStaticArray, `__traits(isStaticArray, T)`)
*/
enum bool isStaticArray(T) = __traits(isStaticArray, T);
@@ -7056,6 +7077,8 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T;
/**
* Detect whether `T` is an associative array type
+ *
+ * See also: $(DDSUBLINK spec/traits, isAssociativeArray, `__traits(isAssociativeArray, T)`)
*/
enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T);
@@ -7542,14 +7565,12 @@ template isCallable(alias callable)
else static if (is(typeof(&callable.opCall) V : V*) && is(V == function))
// T is a type which has a static member function opCall().
enum bool isCallable = true;
- else static if (is(typeof(&callable.opCall!())))
+ else static if (is(typeof(&callable.opCall!()) TemplateInstanceType))
{
- alias TemplateInstanceType = typeof(&callable.opCall!());
enum bool isCallable = isCallable!TemplateInstanceType;
}
- else static if (is(typeof(&callable!())))
+ else static if (is(typeof(&callable!()) TemplateInstanceType))
{
- alias TemplateInstanceType = typeof(&callable!());
enum bool isCallable = isCallable!TemplateInstanceType;
}
else
@@ -7613,14 +7634,15 @@ template isCallable(alias callable)
/**
-Detect whether `T` is an abstract function.
+Detect whether `S` is an abstract function.
+See also: $(DDSUBLINK spec/traits, isAbstractFunction, `__traits(isAbstractFunction, S)`)
Params:
- T = The type to check
+ S = The symbol to check
Returns:
A `bool`
*/
-enum isAbstractFunction(alias T) = __traits(isAbstractFunction, T);
+enum isAbstractFunction(alias S) = __traits(isAbstractFunction, S);
///
@safe unittest
@@ -7635,9 +7657,11 @@ enum isAbstractFunction(alias T) = __traits(isAbstractFunction, T);
}
/**
- * Detect whether `T` is a final function.
+ * Detect whether `S` is a final function.
+ *
+ * See also: $(DDSUBLINK spec/traits, isFinalFunction, `__traits(isFinalFunction, S)`)
*/
-enum isFinalFunction(alias T) = __traits(isFinalFunction, T);
+enum isFinalFunction(alias S) = __traits(isFinalFunction, S);
///
@safe unittest
@@ -7703,9 +7727,11 @@ template isNestedFunction(alias f)
}
/**
- * Detect whether `T` is an abstract class.
+ * Detect whether `S` is an abstract class.
+ *
+ * See also: $(DDSUBLINK spec/traits, isAbstractClass, `__traits(isAbstractClass, S)`)
*/
-enum isAbstractClass(alias T) = __traits(isAbstractClass, T);
+enum isAbstractClass(alias S) = __traits(isAbstractClass, S);
///
@safe unittest
@@ -7723,9 +7749,11 @@ enum isAbstractClass(alias T) = __traits(isAbstractClass, T);
}
/**
- * Detect whether `T` is a final class.
+ * Detect whether `S` is a final class.
+ *
+ * See also: $(DDSUBLINK spec/traits, isFinalClass, `__traits(isFinalClass, S)`)
*/
-enum isFinalClass(alias T) = __traits(isFinalClass, T);
+enum isFinalClass(alias S) = __traits(isFinalClass, S);
///
@safe unittest
@@ -9092,12 +9120,13 @@ template isFinal(alias X)
+ If a type cannot be copied, then code such as `MyStruct x; auto y = x;` will fail to compile.
+ Copying for structs can be disabled by using `@disable this(this)`.
+
+ + See also: $(DDSUBLINK spec/traits, isCopyable, `__traits(isCopyable, S)`)
+ Params:
+ S = The type to check.
+
+ Returns:
+ `true` if `S` can be copied. `false` otherwise.
- + ++/
+ +/
enum isCopyable(S) = __traits(isCopyable, S);
///
diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d
index bde8439..d267e71 100644
--- a/libphobos/src/std/typecons.d
+++ b/libphobos/src/std/typecons.d
@@ -2332,7 +2332,7 @@ break the soundness of D's type system and does not incur any of the
risks usually associated with `cast`.
Params:
- T = An object, interface, array slice type, or associative array type.
+ T = Any type.
*/
template Rebindable(T)
if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
@@ -2395,15 +2395,155 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
static assert(!__traits(compiles, &r.get()));
}
+/// ditto
+struct Rebindable(T)
+if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T)
+{
+private:
+ static if (isAssignable!(typeof(cast() T.init)))
+ {
+ enum useQualifierCast = true;
+
+ typeof(cast() T.init) data;
+ }
+ else
+ {
+ enum useQualifierCast = false;
+
+ align(T.alignof)
+ static struct Payload
+ {
+ static if (hasIndirections!T)
+ {
+ void[T.sizeof] data;
+ }
+ else
+ {
+ ubyte[T.sizeof] data;
+ }
+ }
+
+ Payload data;
+ }
+
+public:
+
+ static if (!__traits(compiles, { T value; }))
+ {
+ @disable this();
+ }
+
+ /**
+ * Constructs a `Rebindable` from a given value.
+ */
+ this(T value) @trusted
+ {
+ static if (useQualifierCast)
+ {
+ this.data = cast() value;
+ }
+ else
+ {
+ set(value);
+ }
+ }
+
+ /**
+ * Overwrites the currently stored value with `value`.
+ */
+ void opAssign(this This)(T value) @trusted
+ {
+ clear;
+ set(value);
+ }
+
+ /**
+ * Returns the value currently stored in the `Rebindable`.
+ */
+ T get(this This)() @property @trusted
+ {
+ static if (useQualifierCast)
+ {
+ return cast(T) this.data;
+ }
+ else
+ {
+ return *cast(T*) &this.data;
+ }
+ }
+
+ static if (!useQualifierCast)
+ {
+ ~this() @trusted
+ {
+ clear;
+ }
+ }
+
+ ///
+ alias get this;
+
+private:
+
+ void set(this This)(T value)
+ {
+ static if (useQualifierCast)
+ {
+ this.data = cast() value;
+ }
+ else
+ {
+ // As we're escaping a copy of `value`, deliberately leak a copy:
+ static union DontCallDestructor
+ {
+ T value;
+ }
+ DontCallDestructor copy = DontCallDestructor(value);
+ this.data = *cast(Payload*) &copy;
+ }
+ }
+
+ void clear(this This)()
+ {
+ // work around reinterpreting cast being impossible in CTFE
+ if (__ctfe)
+ {
+ return;
+ }
+
+ // call possible struct destructors
+ .destroy!(No.initialize)(*cast(T*) &this.data);
+ }
+}
+
+/// Using Rebindable in a generic algorithm:
@safe unittest
{
- class CustomToHash
+ import std.range.primitives : front, popFront;
+
+ // simple version of std.algorithm.searching.maxElement
+ typeof(R.init.front) maxElement(R)(R r)
{
- override size_t toHash() const nothrow @trusted { return 42; }
+ auto max = rebindable(r.front);
+ r.popFront;
+ foreach (e; r)
+ if (e > max)
+ max = e; // Rebindable allows const-correct reassignment
+ return max;
}
- Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash();
- assert(a.toHash() == 42, "Rebindable!A should offer toHash()"
- ~ " by forwarding to A.toHash().");
+ struct S
+ {
+ char[] arr;
+ alias arr this; // for comparison
+ }
+ // can't convert to mutable
+ const S cs;
+ static assert(!__traits(compiles, { S s = cs; }));
+
+ alias CS = const S;
+ CS[] arr = [CS("harp"), CS("apple"), CS("pot")];
+ CS ms = maxElement(arr);
+ assert(ms.arr == "pot");
}
// https://issues.dlang.org/show_bug.cgi?id=18615
@@ -2453,6 +2593,34 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
~ " comparable against Object itself and use Object.opEquals.");
}
+///
+@system unittest
+{
+ static struct S
+ {
+ int* ptr;
+ }
+ S s = S(new int);
+
+ const cs = s;
+ // Can't assign s.ptr to cs.ptr
+ static assert(!__traits(compiles, {s = cs;}));
+
+ Rebindable!(const S) rs = s;
+ assert(rs.ptr is s.ptr);
+ // rs.ptr is const
+ static assert(!__traits(compiles, {rs.ptr = null;}));
+
+ // Can't assign s.ptr to rs.ptr
+ static assert(!__traits(compiles, {s = rs;}));
+
+ const S cs2 = rs;
+ // Rebind rs
+ rs = cs2;
+ rs = S();
+ assert(rs.ptr is null);
+}
+
// https://issues.dlang.org/show_bug.cgi?id=18755
@safe unittest
{
@@ -2473,13 +2641,145 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
}));
}
+@safe unittest
+{
+ class CustomToHash
+ {
+ override size_t toHash() const nothrow @trusted { return 42; }
+ }
+ Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash();
+ assert(a.toHash() == 42, "Rebindable!A should offer toHash()"
+ ~ " by forwarding to A.toHash().");
+}
+
+// Test Rebindable!immutable
+@safe unittest
+{
+ static struct S
+ {
+ int* ptr;
+ }
+ S s = S(new int);
+
+ Rebindable!(immutable S) ri = S(new int);
+ assert(ri.ptr !is null);
+ static assert(!__traits(compiles, {ri.ptr = null;}));
+
+ // ri is not compatible with mutable S
+ static assert(!__traits(compiles, {s = ri;}));
+ static assert(!__traits(compiles, {ri = s;}));
+
+ auto ri2 = ri;
+ assert(ri2.ptr == ri.ptr);
+
+ const S cs3 = ri;
+ static assert(!__traits(compiles, {ri = cs3;}));
+
+ immutable S si = ri;
+ // Rebind ri
+ ri = si;
+ ri = S();
+ assert(ri.ptr is null);
+
+ // Test RB!immutable -> RB!const
+ Rebindable!(const S) rc = ri;
+ assert(rc.ptr is null);
+ ri = S(new int);
+ rc = ri;
+ assert(rc.ptr !is null);
+
+ // test rebindable, opAssign
+ rc.destroy;
+ assert(rc.ptr is null);
+ rc = rebindable(cs3);
+ rc = rebindable(si);
+ assert(rc.ptr !is null);
+
+ ri.destroy;
+ assert(ri.ptr is null);
+ ri = rebindable(si);
+ assert(ri.ptr !is null);
+}
+
+// Test disabled default ctor
+@safe unittest
+{
+ static struct ND
+ {
+ int i;
+ @disable this();
+ this(int i) inout {this.i = i;}
+ }
+ static assert(!__traits(compiles, Rebindable!ND()));
+
+ Rebindable!(const ND) rb = const ND(1);
+ assert(rb.i == 1);
+ rb = immutable ND(2);
+ assert(rb.i == 2);
+ rb = rebindable(const ND(3));
+ assert(rb.i == 3);
+ static assert(!__traits(compiles, rb.i++));
+}
+
+// Test copying
+@safe unittest
+{
+ int del;
+ int post;
+ struct S
+ {
+ int* ptr;
+ int level;
+ this(this)
+ {
+ post++;
+ level++;
+ }
+ ~this()
+ {
+ del++;
+ }
+ }
+
+ // test ref count
+ {
+ Rebindable!S rc = S(new int);
+ }
+ assert(post == del - 1);
+}
+
+@safe unittest
+{
+ int del;
+ int post;
+ struct S
+ {
+ immutable int x;
+ int level;
+ this(this)
+ {
+ post++;
+ level++;
+ }
+ ~this()
+ {
+ del++;
+ }
+ }
+
+ // test ref count
+ {
+ Rebindable!S rc = S(0);
+ }
+ assert(post == del - 1);
+}
+
/**
Convenience function for creating a `Rebindable` using automatic type
inference.
Params:
- obj = A reference to an object, interface, associative array, or an array slice
- to initialize the `Rebindable` with.
+ obj = A reference to a value to initialize the `Rebindable` with.
Returns:
A newly constructed `Rebindable` initialized with the given reference.
@@ -2514,6 +2814,26 @@ if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArr
assert(c3.payload == 2);
}
+/// ditto
+Rebindable!T rebindable(T)(T value)
+if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T
+ && !is(T : Rebindable!U, U))
+{
+ return Rebindable!T(value);
+}
+
+///
+@safe unittest
+{
+ immutable struct S
+ {
+ int[] array;
+ }
+ auto s1 = [3].idup.rebindable;
+ s1 = [4].idup.rebindable;
+ assert(s1 == [4]);
+}
+
/**
This function simply returns the `Rebindable` object passed in. It's useful
in generic programming cases when a given object may be either a regular
@@ -2626,10 +2946,6 @@ Rebindable!T rebindable(T)(Rebindable!T obj)
static assert(is(Rebindable!(T[]) == T[]));
}
- // https://issues.dlang.org/show_bug.cgi?id=12046
- static assert(!__traits(compiles, Rebindable!(int[1])));
- static assert(!__traits(compiles, Rebindable!(const int[1])));
-
// Pull request 3341
Rebindable!(immutable int[int]) pr3341 = [123:345];
assert(pr3341[123] == 345);