summaryrefslogtreecommitdiffstats
path: root/libphobos
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2022-03-29 16:57:10 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2022-04-02 23:56:52 +0200
commit235d5a96cb8dad0b4c427602346fcf966a4ec914 (patch)
treeca19c774a19ad923e5d6f09d43ee8d89c275a96e /libphobos
parentmips: Fix an ICE caused by r12-7962 (diff)
downloadgcc-235d5a96cb8dad0b4c427602346fcf966a4ec914.tar.gz
gcc-235d5a96cb8dad0b4c427602346fcf966a4ec914.tar.bz2
gcc-235d5a96cb8dad0b4c427602346fcf966a4ec914.tar.xz
d: Merge upstream dmd 47871363d, druntime, c52e28b7, phobos 99e9c1b77.
D front-end changes: - Import dmd v2.099.1-beta.1. - The address of NRVO variables is now stored in scoped closures when they have nested references. - Using `__traits(parameters)' in foreach loops now always returns the parameters to the function the foreach appears within. Previously, when used inside a `foreach' using an overloaded `opApply', the trait would yield the parameters to the delegate. - The deprecation period of unannotated `asm' blocks has been ended. - The `inout' attribute no longer implies the `return' attribute. - Added new `D_PreConditions', `D_PostConditions', and `D_Invariants' version identifiers. D runtime changes: - Import druntime v2.099.1-beta.1. Phobos changes: - Import phobos v2.099.1-beta.1. - `Nullable' in `std.typecons' can now act as a range. - std.experimental.logger default level changed to `info' instead of `warning'. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 47871363d. * d-builtins.cc (d_init_versions): Add predefined version identifiers D_PreConditions, D_PostConditions, and D_Invariants. * d-codegen.cc (d_build_call): Update for new front-end interface. (build_frame_type): Generate reference field for NRVO variables with nested references. (build_closure): Generate assignment of return address to closure. * d-tree.h (DECL_INSTANTIATED): Use DECL_LANG_FLAG_2. (bind_expr): Remove. * decl.cc (DeclVisitor::visit (FuncDeclaration *)): Update for new front-end interface. (get_symbol_decl): Likewise. (get_decl_tree): Check DECL_LANG_FRAME_FIELD before DECL_LANG_NRVO. Dereference the field when both are set. * expr.cc (ExprVisitor::visit (DeleteExp *)): Update for new front-end interface. * modules.cc (get_internal_fn): Likewise. * toir.cc (IRVisitor::visit (ReturnStatement *)): Likewise. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime c52e28b7. * libdruntime/Makefile.am (DRUNTIME_DSOURCES_OPENBSD): Add core/sys/openbsd/pwd.d. * libdruntime/Makefile.in: Regenerate. * src/MERGE: Merge upstream phobos 99e9c1b77. * testsuite/libphobos.exceptions/message_with_null.d: New test. gcc/testsuite/ChangeLog: * gdc.dg/nrvo1.d: New test.
Diffstat (limited to 'libphobos')
-rw-r--r--libphobos/libdruntime/MERGE2
-rw-r--r--libphobos/libdruntime/Makefile.am14
-rw-r--r--libphobos/libdruntime/Makefile.in22
-rw-r--r--libphobos/libdruntime/core/atomic.d11
-rw-r--r--libphobos/libdruntime/core/demangle.d2
-rw-r--r--libphobos/libdruntime/core/internal/array/casting.d59
-rw-r--r--libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d11
-rw-r--r--libphobos/libdruntime/core/stdc/stdlib.d4
-rw-r--r--libphobos/libdruntime/core/stdcpp/string.d4
-rw-r--r--libphobos/libdruntime/core/sys/openbsd/pwd.d19
-rw-r--r--libphobos/libdruntime/core/thread/context.d2
-rw-r--r--libphobos/libdruntime/rt/dmain2.d2
-rw-r--r--libphobos/src/MERGE2
-rw-r--r--libphobos/src/std/conv.d24
-rw-r--r--libphobos/src/std/datetime/systime.d29
-rw-r--r--libphobos/src/std/experimental/logger/core.d8
-rw-r--r--libphobos/src/std/experimental/logger/filelogger.d2
-rw-r--r--libphobos/src/std/experimental/logger/multilogger.d2
-rw-r--r--libphobos/src/std/file.d8
-rw-r--r--libphobos/src/std/format/internal/write.d147
-rw-r--r--libphobos/src/std/format/package.d2
-rw-r--r--libphobos/src/std/format/read.d2
-rw-r--r--libphobos/src/std/format/write.d2
-rw-r--r--libphobos/src/std/functional.d12
-rw-r--r--libphobos/src/std/json.d2
-rw-r--r--libphobos/src/std/outbuffer.d4
-rw-r--r--libphobos/src/std/parallelism.d5
-rw-r--r--libphobos/src/std/process.d11
-rw-r--r--libphobos/src/std/socket.d4
-rw-r--r--libphobos/src/std/stdio.d22
-rw-r--r--libphobos/src/std/sumtype.d12
-rw-r--r--libphobos/src/std/typecons.d169
-rw-r--r--libphobos/src/std/uni/package.d44
-rw-r--r--libphobos/src/std/windows/charset.d15
-rw-r--r--libphobos/src/std/windows/syserror.d97
-rw-r--r--libphobos/testsuite/libphobos.exceptions/message_with_null.d8
36 files changed, 580 insertions, 205 deletions
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index 77b6ad00173..b1da32e43e1 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
126b581670ef6e2643d74078f200d1cd21fa40e90 1c52e28b723ccfbe845a95e8e7b528e3cc0b9d790
2 2
3The first line of this file holds the git revision number of the last 3The first line of this file holds the git revision number of the last
4merge done from the dlang/druntime repository. 4merge done from the dlang/druntime repository.
diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am
index ba641315664..6ca4012b713 100644
--- a/libphobos/libdruntime/Makefile.am
+++ b/libphobos/libdruntime/Makefile.am
@@ -294,13 +294,13 @@ DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \
294 294
295DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \ 295DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \
296 core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \ 296 core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \
297 core/sys/openbsd/pthread_np.d core/sys/openbsd/stdlib.d \ 297 core/sys/openbsd/pthread_np.d core/sys/openbsd/pwd.d \
298 core/sys/openbsd/string.d core/sys/openbsd/sys/cdefs.d \ 298 core/sys/openbsd/stdlib.d core/sys/openbsd/string.d \
299 core/sys/openbsd/sys/elf.d core/sys/openbsd/sys/elf32.d \ 299 core/sys/openbsd/sys/cdefs.d core/sys/openbsd/sys/elf.d \
300 core/sys/openbsd/sys/elf64.d core/sys/openbsd/sys/elf_common.d \ 300 core/sys/openbsd/sys/elf32.d core/sys/openbsd/sys/elf64.d \
301 core/sys/openbsd/sys/link_elf.d core/sys/openbsd/sys/mman.d \ 301 core/sys/openbsd/sys/elf_common.d core/sys/openbsd/sys/link_elf.d \
302 core/sys/openbsd/sys/sysctl.d core/sys/openbsd/time.d \ 302 core/sys/openbsd/sys/mman.d core/sys/openbsd/sys/sysctl.d \
303 core/sys/openbsd/unistd.d 303 core/sys/openbsd/time.d core/sys/openbsd/unistd.d
304 304
305DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \ 305DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
306 core/sys/posix/arpa/inet.d core/sys/posix/config.d \ 306 core/sys/posix/arpa/inet.d core/sys/posix/config.d \
diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in
index 1c64d35b164..f7f78d71ff7 100644
--- a/libphobos/libdruntime/Makefile.in
+++ b/libphobos/libdruntime/Makefile.in
@@ -344,9 +344,10 @@ am__objects_14 = core/sys/netbsd/dlfcn.lo core/sys/netbsd/err.lo \
344@DRUNTIME_OS_NETBSD_TRUE@am__objects_15 = $(am__objects_14) 344@DRUNTIME_OS_NETBSD_TRUE@am__objects_15 = $(am__objects_14)
345am__objects_16 = core/sys/openbsd/dlfcn.lo core/sys/openbsd/err.lo \ 345am__objects_16 = core/sys/openbsd/dlfcn.lo core/sys/openbsd/err.lo \
346 core/sys/openbsd/execinfo.lo core/sys/openbsd/pthread_np.lo \ 346 core/sys/openbsd/execinfo.lo core/sys/openbsd/pthread_np.lo \
347 core/sys/openbsd/stdlib.lo core/sys/openbsd/string.lo \ 347 core/sys/openbsd/pwd.lo core/sys/openbsd/stdlib.lo \
348 core/sys/openbsd/sys/cdefs.lo core/sys/openbsd/sys/elf.lo \ 348 core/sys/openbsd/string.lo core/sys/openbsd/sys/cdefs.lo \
349 core/sys/openbsd/sys/elf32.lo core/sys/openbsd/sys/elf64.lo \ 349 core/sys/openbsd/sys/elf.lo core/sys/openbsd/sys/elf32.lo \
350 core/sys/openbsd/sys/elf64.lo \
350 core/sys/openbsd/sys/elf_common.lo \ 351 core/sys/openbsd/sys/elf_common.lo \
351 core/sys/openbsd/sys/link_elf.lo core/sys/openbsd/sys/mman.lo \ 352 core/sys/openbsd/sys/link_elf.lo core/sys/openbsd/sys/mman.lo \
352 core/sys/openbsd/sys/sysctl.lo core/sys/openbsd/time.lo \ 353 core/sys/openbsd/sys/sysctl.lo core/sys/openbsd/time.lo \
@@ -958,13 +959,13 @@ DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \
958 959
959DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \ 960DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \
960 core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \ 961 core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \
961 core/sys/openbsd/pthread_np.d core/sys/openbsd/stdlib.d \ 962 core/sys/openbsd/pthread_np.d core/sys/openbsd/pwd.d \
962 core/sys/openbsd/string.d core/sys/openbsd/sys/cdefs.d \ 963 core/sys/openbsd/stdlib.d core/sys/openbsd/string.d \
963 core/sys/openbsd/sys/elf.d core/sys/openbsd/sys/elf32.d \ 964 core/sys/openbsd/sys/cdefs.d core/sys/openbsd/sys/elf.d \
964 core/sys/openbsd/sys/elf64.d core/sys/openbsd/sys/elf_common.d \ 965 core/sys/openbsd/sys/elf32.d core/sys/openbsd/sys/elf64.d \
965 core/sys/openbsd/sys/link_elf.d core/sys/openbsd/sys/mman.d \ 966 core/sys/openbsd/sys/elf_common.d core/sys/openbsd/sys/link_elf.d \
966 core/sys/openbsd/sys/sysctl.d core/sys/openbsd/time.d \ 967 core/sys/openbsd/sys/mman.d core/sys/openbsd/sys/sysctl.d \
967 core/sys/openbsd/unistd.d 968 core/sys/openbsd/time.d core/sys/openbsd/unistd.d
968 969
969DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \ 970DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
970 core/sys/posix/arpa/inet.d core/sys/posix/config.d \ 971 core/sys/posix/arpa/inet.d core/sys/posix/config.d \
@@ -1619,6 +1620,7 @@ core/sys/openbsd/dlfcn.lo: core/sys/openbsd/$(am__dirstamp)
1619core/sys/openbsd/err.lo: core/sys/openbsd/$(am__dirstamp) 1620core/sys/openbsd/err.lo: core/sys/openbsd/$(am__dirstamp)
1620core/sys/openbsd/execinfo.lo: core/sys/openbsd/$(am__dirstamp) 1621core/sys/openbsd/execinfo.lo: core/sys/openbsd/$(am__dirstamp)
1621core/sys/openbsd/pthread_np.lo: core/sys/openbsd/$(am__dirstamp) 1622core/sys/openbsd/pthread_np.lo: core/sys/openbsd/$(am__dirstamp)
1623core/sys/openbsd/pwd.lo: core/sys/openbsd/$(am__dirstamp)
1622core/sys/openbsd/stdlib.lo: core/sys/openbsd/$(am__dirstamp) 1624core/sys/openbsd/stdlib.lo: core/sys/openbsd/$(am__dirstamp)
1623core/sys/openbsd/string.lo: core/sys/openbsd/$(am__dirstamp) 1625core/sys/openbsd/string.lo: core/sys/openbsd/$(am__dirstamp)
1624core/sys/openbsd/sys/$(am__dirstamp): 1626core/sys/openbsd/sys/$(am__dirstamp):
diff --git a/libphobos/libdruntime/core/atomic.d b/libphobos/libdruntime/core/atomic.d
index e6a82e58f85..4af3fdf2bd2 100644
--- a/libphobos/libdruntime/core/atomic.d
+++ b/libphobos/libdruntime/core/atomic.d
@@ -292,7 +292,7 @@ template cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.
292 in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") 292 in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
293 { 293 {
294 // resolve implicit conversions 294 // resolve implicit conversions
295 T arg1 = ifThis; 295 const T arg1 = ifThis;
296 T arg2 = writeThis; 296 T arg2 = writeThis;
297 297
298 static if (__traits(isFloating, T)) 298 static if (__traits(isFloating, T))
@@ -1276,4 +1276,13 @@ version (CoreUnittest)
1276 shared NoIndirections n; 1276 shared NoIndirections n;
1277 static assert(is(typeof(atomicLoad(n)) == NoIndirections)); 1277 static assert(is(typeof(atomicLoad(n)) == NoIndirections));
1278 } 1278 }
1279
1280 unittest // Issue 21631
1281 {
1282 shared uint si1 = 45;
1283 shared uint si2 = 38;
1284 shared uint* psi = &si1;
1285
1286 assert((&psi).cas(cast(const) psi, &si2));
1287 }
1279} 1288}
diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d
index 930e0cd9c24..cb8d433b74b 100644
--- a/libphobos/libdruntime/core/demangle.d
+++ b/libphobos/libdruntime/core/demangle.d
@@ -1339,7 +1339,7 @@ pure @safe:
1339 TypeFunction: 1339 TypeFunction:
1340 CallConvention FuncAttrs Arguments ArgClose Type 1340 CallConvention FuncAttrs Arguments ArgClose Type
1341 */ 1341 */
1342 char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no ) return 1342 char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no ) return scope
1343 { 1343 {
1344 debug(trace) printf( "parseTypeFunction+\n" ); 1344 debug(trace) printf( "parseTypeFunction+\n" );
1345 debug(trace) scope(success) printf( "parseTypeFunction-\n" ); 1345 debug(trace) scope(success) printf( "parseTypeFunction-\n" );
diff --git a/libphobos/libdruntime/core/internal/array/casting.d b/libphobos/libdruntime/core/internal/array/casting.d
index e862f8eb96a..4366da829c9 100644
--- a/libphobos/libdruntime/core/internal/array/casting.d
+++ b/libphobos/libdruntime/core/internal/array/casting.d
@@ -17,12 +17,13 @@ builds. It is separate from `__ArrayCast` to minimize code
17bloat. 17bloat.
18 18
19Params: 19Params:
20 fromType = name of the type being cast from 20 fromType = name of the type being cast from
21 fromSize = total size in bytes of the array being cast from 21 fromSize = total size in bytes of the array being cast from
22 toType = name of the type being cast o 22 fromLength = length of array being cast from
23 toSize = total size in bytes of the array being cast to 23 toType = name of the type being cast to
24 toElemSize = element size of array being cast to
24 */ 25 */
25private void onArrayCastError()(string fromType, size_t fromSize, string toType, size_t toSize) @trusted 26private void onArrayCastError()(string fromType, size_t fromSize, size_t fromLength, string toType, size_t toElemSize) @trusted
26{ 27{
27 import core.internal.string : unsignedToTempString; 28 import core.internal.string : unsignedToTempString;
28 import core.memory : pureMalloc; 29 import core.memory : pureMalloc;
@@ -45,17 +46,22 @@ private void onArrayCastError()(string fromType, size_t fromSize, string toType,
45 index += N; 46 index += N;
46 } 47 }
47 48
48 add("An array of size "); 49 add("`");
49 auto s = unsignedToTempString(fromSize); 50 add(fromType);
51 add("[]` of length ");
52 auto s = unsignedToTempString(fromLength);
50 add(s[]); 53 add(s[]);
51 add(" does not align on an array of size "); 54 add(" cannot be cast to `");
52 s = unsignedToTempString(toSize); 55 add(toType);
56 add("[]` as its length in bytes (");
57 s = unsignedToTempString(fromSize);
53 add(s[]); 58 add(s[]);
54 add(", so `"); 59 add(") is not a multiple of `");
55 add(fromType);
56 add("` cannot be cast to `");
57 add(toType); 60 add(toType);
58 add("`"); 61 add(".sizeof` (");
62 s = unsignedToTempString(toElemSize);
63 add(s[]);
64 add(").");
59 msg[index] = '\0'; // null-termination 65 msg[index] = '\0'; // null-termination
60 66
61 // first argument must evaluate to `false` at compile-time to maintain memory safety in release builds 67 // first argument must evaluate to `false` at compile-time to maintain memory safety in release builds
@@ -64,7 +70,7 @@ private void onArrayCastError()(string fromType, size_t fromSize, string toType,
64 70
65/** 71/**
66The compiler lowers expressions of `cast(TTo[])TFrom[]` to 72The compiler lowers expressions of `cast(TTo[])TFrom[]` to
67this implementation. 73this implementation. Note that this does not detect alignment problems.
68 74
69Params: 75Params:
70 from = the array to reinterpret-cast 76 from = the array to reinterpret-cast
@@ -79,7 +85,7 @@ TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted
79 85
80 if ((fromSize % TTo.sizeof) != 0) 86 if ((fromSize % TTo.sizeof) != 0)
81 { 87 {
82 onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof); 88 onArrayCastError(TFrom.stringof, fromSize, from.length, TTo.stringof, TTo.sizeof);
83 } 89 }
84 90
85 struct Array 91 struct Array
@@ -113,3 +119,26 @@ TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted
113 foreach (v; s) 119 foreach (v; s)
114 assert(v == cast(short) 0xabab); 120 assert(v == cast(short) 0xabab);
115} 121}
122
123@system nothrow unittest
124{
125 string msg;
126 try
127 {
128 auto str = "hello";
129 auto wstr = cast(wstring) str;
130 }
131 catch (Throwable t)
132 msg = t.msg;
133
134 static immutable expected = "`immutable(char)[]` of length 5 cannot be cast to `immutable(wchar)[]` as " ~
135 "its length in bytes (5) is not a multiple of `immutable(wchar).sizeof` (2).";
136
137 if (msg != expected)
138 {
139 import core.stdc.stdio;
140 printf("Expected: |%.*s|\n", cast(int) expected.length, expected.ptr);
141 printf("Actual : |%.*s|\n", cast(int) msg.length, msg.ptr);
142 assert(false);
143 }
144}
diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
index aa51867fc2c..e29e42648b6 100644
--- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
+++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
@@ -2935,19 +2935,18 @@ struct Gcx
2935 else version (linux) 2935 else version (linux)
2936 { 2936 {
2937 // clone() fits better as we don't want to do anything but scanning in the child process. 2937 // clone() fits better as we don't want to do anything but scanning in the child process.
2938 // no fork-handlera are called, so we can avoid deadlocks due to malloc locks. Probably related: 2938 // no fork-handlers are called, so we can avoid deadlocks due to malloc locks. Probably related:
2939 // https://sourceware.org/bugzilla/show_bug.cgi?id=4737 2939 // https://sourceware.org/bugzilla/show_bug.cgi?id=4737
2940 import core.sys.linux.sched : clone; 2940 import core.sys.linux.sched : clone;
2941 import core.sys.posix.signal : SIGCHLD; 2941 import core.sys.posix.signal : SIGCHLD;
2942 enum CLONE_CHILD_CLEARTID = 0x00200000; /* Register exit futex and memory */ 2942 const flags = SIGCHLD; // exit signal
2943 const flags = CLONE_CHILD_CLEARTID | SIGCHLD; // child thread id not needed
2944 scope int delegate() scope dg = &child_mark; 2943 scope int delegate() scope dg = &child_mark;
2945 extern(C) static int wrap_delegate(void* arg) 2944 extern(C) static int wrap_delegate(void* arg)
2946 { 2945 {
2947 auto dg = cast(int delegate() scope*)arg; 2946 auto dg = cast(int delegate() scope*)arg;
2948 return (*dg)(); 2947 return (*dg)();
2949 } 2948 }
2950 char[256] stackbuf; // enough stack space for clone() to place some info for the child without stomping the parent stack 2949 ubyte[256] stackbuf; // enough stack space for clone() to place some info for the child without stomping the parent stack
2951 auto stack = stackbuf.ptr + (isStackGrowingDown ? stackbuf.length : 0); 2950 auto stack = stackbuf.ptr + (isStackGrowingDown ? stackbuf.length : 0);
2952 auto pid = clone(&wrap_delegate, stack, flags, &dg); 2951 auto pid = clone(&wrap_delegate, stack, flags, &dg);
2953 } 2952 }
@@ -2957,7 +2956,6 @@ struct Gcx
2957 auto pid = fork(); 2956 auto pid = fork();
2958 fork_needs_lock = true; 2957 fork_needs_lock = true;
2959 } 2958 }
2960 assert(pid != -1);
2961 switch (pid) 2959 switch (pid)
2962 { 2960 {
2963 case -1: // fork() failed, retry without forking 2961 case -1: // fork() failed, retry without forking
@@ -3028,7 +3026,7 @@ struct Gcx
3028 //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr); 3026 //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr);
3029 3027
3030 version (COLLECT_FORK) 3028 version (COLLECT_FORK)
3031 bool doFork = shouldFork; 3029 alias doFork = shouldFork;
3032 else 3030 else
3033 enum doFork = false; 3031 enum doFork = false;
3034 3032
@@ -3083,6 +3081,7 @@ Lmark:
3083 final switch (forkResult) 3081 final switch (forkResult)
3084 { 3082 {
3085 case ChildStatus.error: 3083 case ChildStatus.error:
3084 // fork() failed, retry without forking
3086 disableFork(); 3085 disableFork();
3087 goto Lmark; 3086 goto Lmark;
3088 case ChildStatus.running: 3087 case ChildStatus.running:
diff --git a/libphobos/libdruntime/core/stdc/stdlib.d b/libphobos/libdruntime/core/stdc/stdlib.d
index 92f8f705540..dbe55a4b340 100644
--- a/libphobos/libdruntime/core/stdc/stdlib.d
+++ b/libphobos/libdruntime/core/stdc/stdlib.d
@@ -69,8 +69,8 @@ struct div_t
69/// 69///
70struct ldiv_t 70struct ldiv_t
71{ 71{
72 int quot, 72 c_long quot,
73 rem; 73 rem;
74} 74}
75 75
76/// 76///
diff --git a/libphobos/libdruntime/core/stdcpp/string.d b/libphobos/libdruntime/core/stdcpp/string.d
index dfec1ec9f7e..defbd8330eb 100644
--- a/libphobos/libdruntime/core/stdcpp/string.d
+++ b/libphobos/libdruntime/core/stdcpp/string.d
@@ -343,7 +343,7 @@ extern(D):
343 /// 343 ///
344 inout(T)* data() inout @safe { return _Get_data()._Myptr; } 344 inout(T)* data() inout @safe { return _Get_data()._Myptr; }
345 /// 345 ///
346 inout(T)[] as_array() return scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } 346 inout(T)[] as_array() scope return inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
347 /// 347 ///
348 ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } 348 ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; }
349 349
@@ -1920,7 +1920,7 @@ extern(D):
1920 /// 1920 ///
1921 inout(T)* data() inout @safe { return __get_pointer(); } 1921 inout(T)* data() inout @safe { return __get_pointer(); }
1922 /// 1922 ///
1923 inout(T)[] as_array() return scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; } 1923 inout(T)[] as_array() scope return inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
1924 /// 1924 ///
1925 ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; } 1925 ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; }
1926 1926
diff --git a/libphobos/libdruntime/core/sys/openbsd/pwd.d b/libphobos/libdruntime/core/sys/openbsd/pwd.d
new file mode 100644
index 00000000000..f6a35a89110
--- /dev/null
+++ b/libphobos/libdruntime/core/sys/openbsd/pwd.d
@@ -0,0 +1,19 @@
1/**
2 * D header file for OpenBSD pwd.h.
3 *
4 * Copyright: Copyright © 2022, The D Language Foundation
5 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
6 * Authors: Brian Callahan
7 */
8module core.sys.openbsd.pwd;
9
10version (OpenBSD):
11extern (C):
12nothrow:
13@nogc:
14
15public import core.sys.posix.pwd;
16import core.sys.posix.sys.types : uid_t;
17
18passwd* getpwnam_shadow(scope const char*);
19passwd* getpwuid_shadow(uid_t);
diff --git a/libphobos/libdruntime/core/thread/context.d b/libphobos/libdruntime/core/thread/context.d
index 1b3c0cabbba..e477269b849 100644
--- a/libphobos/libdruntime/core/thread/context.d
+++ b/libphobos/libdruntime/core/thread/context.d
@@ -6,7 +6,7 @@
6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7 * (See accompanying file LICENSE) 7 * (See accompanying file LICENSE)
8 * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak 8 * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak
9 * Source: $(DRUNTIMESRC core/thread/package.d) 9 * Source: $(DRUNTIMESRC core/thread/context.d)
10 */ 10 */
11 11
12module core.thread.context; 12module core.thread.context;
diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d
index 47b67f130b8..5ef26954890 100644
--- a/libphobos/libdruntime/rt/dmain2.d
+++ b/libphobos/libdruntime/rt/dmain2.d
@@ -669,7 +669,7 @@ extern (C) void _d_print_throwable(Throwable t)
669 669
670 void sink(in char[] buf) scope nothrow 670 void sink(in char[] buf) scope nothrow
671 { 671 {
672 fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr); 672 fwrite(buf.ptr, char.sizeof, buf.length, stderr);
673 } 673 }
674 formatThrowable(t, &sink); 674 formatThrowable(t, &sink);
675} 675}
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 6eb555ed29b..7306c1071a1 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
1a74fa63e6775d626850d8ebd854d9803c7ffb97d 199e9c1b7741e0f4e6f2a8c14883c4828d092701d
2 2
3The first line of this file holds the git revision number of the last 3The first line of this file holds the git revision number of the last
4merge done from the dlang/phobos repository. 4merge done from the dlang/phobos repository.
diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d
index 06b37978ba6..8f6c3bf568e 100644
--- a/libphobos/src/std/conv.d
+++ b/libphobos/src/std/conv.d
@@ -2850,7 +2850,7 @@ do
2850 static if (isNarrowString!Source) 2850 static if (isNarrowString!Source)
2851 { 2851 {
2852 import std.string : representation; 2852 import std.string : representation;
2853 auto s = source.representation; 2853 scope s = source.representation;
2854 } 2854 }
2855 else 2855 else
2856 { 2856 {
@@ -2898,7 +2898,7 @@ do
2898 } 2898 }
2899 2899
2900 static if (isNarrowString!Source) 2900 static if (isNarrowString!Source)
2901 source = cast(Source) s; 2901 source = source[$ - s.length .. $];
2902 2902
2903 static if (doCount) 2903 static if (doCount)
2904 { 2904 {
@@ -3105,7 +3105,7 @@ if (isSomeString!Source && !is(Source == enum) &&
3105 * A $(LREF ConvException) if `source` is empty, if no number could be 3105 * A $(LREF ConvException) if `source` is empty, if no number could be
3106 * parsed, or if an overflow occurred. 3106 * parsed, or if an overflow occurred.
3107 */ 3107 */
3108auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref scope Source source) 3108auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source)
3109if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && 3109if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
3110 isFloatingPoint!Target && !is(Target == enum)) 3110 isFloatingPoint!Target && !is(Target == enum))
3111{ 3111{
@@ -3115,18 +3115,17 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
3115 static if (isNarrowString!Source) 3115 static if (isNarrowString!Source)
3116 { 3116 {
3117 import std.string : representation; 3117 import std.string : representation;
3118 auto p = source.representation; 3118 scope p = source.representation;
3119 } 3119 }
3120 else 3120 else
3121 { 3121 {
3122 alias p = source; 3122 alias p = source;
3123 } 3123 }
3124 3124
3125 void advanceSource() @trusted 3125 void advanceSource()
3126 { 3126 {
3127 // p is assigned from source.representation above so the cast is valid
3128 static if (isNarrowString!Source) 3127 static if (isNarrowString!Source)
3129 source = cast(Source) p; 3128 source = source[$ - p.length .. $];
3130 } 3129 }
3131 3130
3132 static immutable real[14] negtab = 3131 static immutable real[14] negtab =
@@ -5983,3 +5982,14 @@ if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) &&
5983 } 5982 }
5984 } 5983 }
5985} 5984}
5985
5986// Converts an unsigned integer to a compile-time string constant.
5987package enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length];
5988
5989// Check that .stringof does what we expect, since it's not guaranteed by the
5990// language spec.
5991@safe /*@betterC*/ unittest
5992{
5993 assert(toCtString!0 == "0");
5994 assert(toCtString!123456 == "123456");
5995}
diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d
index 949ad7d75cc..e80f1225155 100644
--- a/libphobos/src/std/datetime/systime.d
+++ b/libphobos/src/std/datetime/systime.d
@@ -390,6 +390,33 @@ public:
390 hnsecsToUnixEpoch; 390 hnsecsToUnixEpoch;
391 } 391 }
392 } 392 }
393 else version (Hurd)
394 {
395 static if (clockType == ClockType.second)
396 return unixTimeToStdTime(core.stdc.time.time(null));
397 else
398 {
399 import core.sys.hurd.time : CLOCK_REALTIME_COARSE;
400 import core.sys.posix.time : clock_gettime, CLOCK_REALTIME;
401 static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE;
402 else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME;
403 else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME;
404 else static assert(0, "Previous static if is wrong.");
405 timespec ts = void;
406 immutable error = clock_gettime(clockArg, &ts);
407 // Posix clock_gettime called with a valid address and valid clock_id is only
408 // permitted to fail if the number of seconds does not fit in time_t. If tv_sec
409 // is long or larger overflow won't happen before 292 billion years A.D.
410 static if (ts.tv_sec.max < long.max)
411 {
412 if (error)
413 throw new TimeException("Call to clock_gettime() failed");
414 }
415 return convert!("seconds", "hnsecs")(ts.tv_sec) +
416 ts.tv_nsec / 100 +
417 hnsecsToUnixEpoch;
418 }
419 }
393 else static assert(0, "Unsupported OS"); 420 else static assert(0, "Unsupported OS");
394 } 421 }
395 else static assert(0, "Unsupported OS"); 422 else static assert(0, "Unsupported OS");
@@ -693,7 +720,7 @@ public:
693 720
694 Returns: The `this` of this `SysTime`. 721 Returns: The `this` of this `SysTime`.
695 +/ 722 +/
696 ref SysTime opAssign()(auto ref const(SysTime) rhs) return scope @safe pure nothrow 723 ref SysTime opAssign()(auto ref const(SysTime) rhs) scope return @safe pure nothrow
697 { 724 {
698 _stdTime = rhs._stdTime; 725 _stdTime = rhs._stdTime;
699 _timezone = rhs._timezone; 726 _timezone = rhs._timezone;
diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d
index afd98add6ba..6a28de52ed0 100644
--- a/libphobos/src/std/experimental/logger/core.d
+++ b/libphobos/src/std/experimental/logger/core.d
@@ -1633,14 +1633,14 @@ private @property Logger defaultSharedLoggerImpl() @trusted
1633 import std.concurrency : initOnce; 1633 import std.concurrency : initOnce;
1634 initOnce!stdSharedDefaultLogger({ 1634 initOnce!stdSharedDefaultLogger({
1635 auto buffer = cast(ubyte[]) _buffer; 1635 auto buffer = cast(ubyte[]) _buffer;
1636 return emplace!FileLogger(buffer, stderr, LogLevel.warning); 1636 return emplace!FileLogger(buffer, stderr, LogLevel.info);
1637 }()); 1637 }());
1638 1638
1639 return stdSharedDefaultLogger; 1639 return stdSharedDefaultLogger;
1640} 1640}
1641 1641
1642/** This property sets and gets the default `Logger`. Unless set to another 1642/** This property sets and gets the default `Logger`. Unless set to another
1643logger by the user, the default logger's log level is LogLevel.warning. 1643logger by the user, the default logger's log level is LogLevel.info.
1644 1644
1645Example: 1645Example:
1646------------- 1646-------------
@@ -2008,7 +2008,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe
2008 2008
2009 auto oldunspecificLogger = sharedLog; 2009 auto oldunspecificLogger = sharedLog;
2010 2010
2011 assert(oldunspecificLogger.logLevel == LogLevel.warning, 2011 assert(oldunspecificLogger.logLevel == LogLevel.info,
2012 to!string(oldunspecificLogger.logLevel)); 2012 to!string(oldunspecificLogger.logLevel));
2013 2013
2014 assert(l.logLevel == LogLevel.all); 2014 assert(l.logLevel == LogLevel.all);
@@ -3064,7 +3064,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted
3064{ 3064{
3065 auto dl = cast(FileLogger) sharedLog; 3065 auto dl = cast(FileLogger) sharedLog;
3066 assert(dl !is null); 3066 assert(dl !is null);
3067 assert(dl.logLevel == LogLevel.warning); 3067 assert(dl.logLevel == LogLevel.info);
3068 assert(globalLogLevel == LogLevel.all); 3068 assert(globalLogLevel == LogLevel.all);
3069 3069
3070 auto tl = cast(StdForwardLogger) stdThreadLocalLog; 3070 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d
index a0bea7733cf..5112e520bc0 100644
--- a/libphobos/src/std/experimental/logger/filelogger.d
+++ b/libphobos/src/std/experimental/logger/filelogger.d
@@ -263,7 +263,7 @@ class FileLogger : Logger
263{ 263{
264 auto dl = cast(FileLogger) sharedLog; 264 auto dl = cast(FileLogger) sharedLog;
265 assert(dl !is null); 265 assert(dl !is null);
266 assert(dl.logLevel == LogLevel.warning); 266 assert(dl.logLevel == LogLevel.info);
267 assert(globalLogLevel == LogLevel.all); 267 assert(globalLogLevel == LogLevel.all);
268 268
269 auto tl = cast(StdForwardLogger) stdThreadLocalLog; 269 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d
index 90bfb5820ab..9acd23a59cd 100644
--- a/libphobos/src/std/experimental/logger/multilogger.d
+++ b/libphobos/src/std/experimental/logger/multilogger.d
@@ -191,7 +191,7 @@ class MultiLogger : Logger
191{ 191{
192 auto dl = cast(FileLogger) sharedLog; 192 auto dl = cast(FileLogger) sharedLog;
193 assert(dl !is null); 193 assert(dl !is null);
194 assert(dl.logLevel == LogLevel.warning); 194 assert(dl.logLevel == LogLevel.info);
195 assert(globalLogLevel == LogLevel.all); 195 assert(globalLogLevel == LogLevel.all);
196 196
197 auto tl = cast(StdForwardLogger) stdThreadLocalLog; 197 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d
index b09b82ab85e..6bc7d4d9113 100644
--- a/libphobos/src/std/file.d
+++ b/libphobos/src/std/file.d
@@ -214,7 +214,7 @@ class FileException : Exception
214 string file = __FILE__, 214 string file = __FILE__,
215 size_t line = __LINE__) @safe 215 size_t line = __LINE__) @safe
216 { 216 {
217 this(name, sysErrorString(errno), file, line, errno); 217 this(name, generateSysErrorMsg(errno), file, line, errno);
218 } 218 }
219 else version (Posix) this(scope const(char)[] name, 219 else version (Posix) this(scope const(char)[] name,
220 uint errno = .errno, 220 uint errno = .errno,
@@ -3471,7 +3471,7 @@ else version (Posix) string getcwd() @trusted
3471 while (true) 3471 while (true)
3472 { 3472 {
3473 auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length); 3473 auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
3474 enforce(len, sysErrorString(GetLastError())); 3474 wenforce(len);
3475 if (len != buffer.length) 3475 if (len != buffer.length)
3476 return to!(string)(buffer[0 .. len]); 3476 return to!(string)(buffer[0 .. len]);
3477 buffer.length *= 2; 3477 buffer.length *= 2;
@@ -3574,6 +3574,10 @@ else version (Posix) string getcwd() @trusted
3574 // Only Solaris 10 and later 3574 // Only Solaris 10 and later
3575 return readLink(format("/proc/%d/path/a.out", getpid())); 3575 return readLink(format("/proc/%d/path/a.out", getpid()));
3576 } 3576 }
3577 else version (Hurd)
3578 {
3579 return readLink("/proc/self/exe");
3580 }
3577 else 3581 else
3578 static assert(0, "thisExePath is not supported on this platform"); 3582 static assert(0, "thisExePath is not supported on this platform");
3579} 3583}
diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d
index 68b9e4daccc..f1d69643495 100644
--- a/libphobos/src/std/format/internal/write.d
+++ b/libphobos/src/std/format/internal/write.d
@@ -2000,8 +2000,7 @@ template hasToString(T, Char)
2000 enum hasToString = HasToStringResult.none; 2000 enum hasToString = HasToStringResult.none;
2001 } 2001 }
2002 else static if (is(typeof( 2002 else static if (is(typeof(
2003 { 2003 (T val) {
2004 T val = void;
2005 const FormatSpec!Char f; 2004 const FormatSpec!Char f;
2006 static struct S {void put(scope Char s){}} 2005 static struct S {void put(scope Char s){}}
2007 S s; 2006 S s;
@@ -2015,8 +2014,7 @@ template hasToString(T, Char)
2015 enum hasToString = HasToStringResult.customPutWriterFormatSpec; 2014 enum hasToString = HasToStringResult.customPutWriterFormatSpec;
2016 } 2015 }
2017 else static if (is(typeof( 2016 else static if (is(typeof(
2018 { 2017 (T val) {
2019 T val = void;
2020 static struct S {void put(scope Char s){}} 2018 static struct S {void put(scope Char s){}}
2021 S s; 2019 S s;
2022 val.toString(s); 2020 val.toString(s);
@@ -2026,36 +2024,36 @@ template hasToString(T, Char)
2026 { 2024 {
2027 enum hasToString = HasToStringResult.customPutWriter; 2025 enum hasToString = HasToStringResult.customPutWriter;
2028 } 2026 }
2029 else static if (is(typeof({ T val = void; FormatSpec!Char f; val.toString((scope const(char)[] s){}, f); }))) 2027 else static if (is(typeof((T val) { FormatSpec!Char f; val.toString((scope const(char)[] s){}, f); })))
2030 { 2028 {
2031 enum hasToString = HasToStringResult.constCharSinkFormatSpec; 2029 enum hasToString = HasToStringResult.constCharSinkFormatSpec;
2032 } 2030 }
2033 else static if (is(typeof({ T val = void; val.toString((scope const(char)[] s){}, "%s"); }))) 2031 else static if (is(typeof((T val) { val.toString((scope const(char)[] s){}, "%s"); })))
2034 { 2032 {
2035 enum hasToString = HasToStringResult.constCharSinkFormatString; 2033 enum hasToString = HasToStringResult.constCharSinkFormatString;
2036 } 2034 }
2037 else static if (is(typeof({ T val = void; val.toString((scope const(char)[] s){}); }))) 2035 else static if (is(typeof((T val) { val.toString((scope const(char)[] s){}); })))
2038 { 2036 {
2039 enum hasToString = HasToStringResult.constCharSink; 2037 enum hasToString = HasToStringResult.constCharSink;
2040 } 2038 }
2041 2039
2042 else static if (hasPreviewIn && 2040 else static if (hasPreviewIn &&
2043 is(typeof({ T val = void; FormatSpec!Char f; val.toString((in char[] s){}, f); }))) 2041 is(typeof((T val) { FormatSpec!Char f; val.toString((in char[] s){}, f); })))
2044 { 2042 {
2045 enum hasToString = HasToStringResult.inCharSinkFormatSpec; 2043 enum hasToString = HasToStringResult.inCharSinkFormatSpec;
2046 } 2044 }
2047 else static if (hasPreviewIn && 2045 else static if (hasPreviewIn &&
2048 is(typeof({ T val = void; val.toString((in char[] s){}, "%s"); }))) 2046 is(typeof((T val) { val.toString((in char[] s){}, "%s"); })))
2049 { 2047 {
2050 enum hasToString = HasToStringResult.inCharSinkFormatString; 2048 enum hasToString = HasToStringResult.inCharSinkFormatString;
2051 } 2049 }
2052 else static if (hasPreviewIn && 2050 else static if (hasPreviewIn &&
2053 is(typeof({ T val = void; val.toString((in char[] s){}); }))) 2051 is(typeof((T val) { val.toString((in char[] s){}); })))
2054 { 2052 {
2055 enum hasToString = HasToStringResult.inCharSink; 2053 enum hasToString = HasToStringResult.inCharSink;
2056 } 2054 }
2057 2055
2058 else static if (is(typeof({ T val = void; return val.toString(); }()) S) && isSomeString!S) 2056 else static if (is(ReturnType!((T val) { return val.toString(); }) S) && isSomeString!S)
2059 { 2057 {
2060 enum hasToString = HasToStringResult.hasSomeToString; 2058 enum hasToString = HasToStringResult.hasSomeToString;
2061 } 2059 }
@@ -2171,6 +2169,133 @@ template hasToString(T, Char)
2171 } 2169 }
2172} 2170}
2173 2171
2172// const toString methods
2173@safe unittest
2174{
2175 import std.range.primitives : isOutputRange;
2176
2177 static struct A
2178 {
2179 void toString(Writer)(ref Writer w) const
2180 if (isOutputRange!(Writer, string))
2181 {}
2182 }
2183 static struct B
2184 {
2185 void toString(scope void delegate(scope const(char)[]) sink, scope FormatSpec!char fmt) const {}
2186 }
2187 static struct C
2188 {
2189 void toString(scope void delegate(scope const(char)[]) sink, string fmt) const {}
2190 }
2191 static struct D
2192 {
2193 void toString(scope void delegate(scope const(char)[]) sink) const {}
2194 }
2195 static struct E
2196 {
2197 string toString() const {return "";}
2198 }
2199 static struct F
2200 {
2201 void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const
2202 if (isOutputRange!(Writer, string))
2203 {}
2204 }
2205 static struct G
2206 {
2207 string toString() const {return "";}
2208 void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {}
2209 }
2210 static struct H
2211 {
2212 string toString() const {return "";}
2213 void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const
2214 if (isOutputRange!(Writer, string))
2215 {}
2216 }
2217 static struct I
2218 {
2219 void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {}
2220 void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const
2221 if (isOutputRange!(Writer, string))
2222 {}
2223 }
2224 static struct J
2225 {
2226 string toString() const {return "";}
2227 void toString(Writer)(ref Writer w, scope ref FormatSpec!char fmt) const
2228 if (isOutputRange!(Writer, string))
2229 {}
2230 }
2231 static struct K
2232 {
2233 void toString(Writer)(Writer w, scope const ref FormatSpec!char fmt) const
2234 if (isOutputRange!(Writer, string))
2235 {}
2236 }
2237 static struct L
2238 {
2239 void toString(Writer)(ref Writer w, scope const FormatSpec!char fmt) const
2240 if (isOutputRange!(Writer, string))
2241 {}
2242 }
2243 static struct M
2244 {
2245 void toString(scope void delegate(in char[]) sink, in FormatSpec!char fmt) const {}
2246 }
2247 static struct N
2248 {
2249 void toString(scope void delegate(in char[]) sink, string fmt) const {}
2250 }
2251 static struct O
2252 {
2253 void toString(scope void delegate(in char[]) sink) const {}
2254 }
2255
2256 with(HasToStringResult)
2257 {
2258 static assert(hasToString!(A, char) == customPutWriter);
2259 static assert(hasToString!(B, char) == constCharSinkFormatSpec);
2260 static assert(hasToString!(C, char) == constCharSinkFormatString);
2261 static assert(hasToString!(D, char) == constCharSink);
2262 static assert(hasToString!(E, char) == hasSomeToString);
2263 static assert(hasToString!(F, char) == customPutWriterFormatSpec);
2264 static assert(hasToString!(G, char) == customPutWriter);
2265 static assert(hasToString!(H, char) == customPutWriterFormatSpec);
2266 static assert(hasToString!(I, char) == customPutWriterFormatSpec);
2267 static assert(hasToString!(J, char) == hasSomeToString);
2268 static assert(hasToString!(K, char) == constCharSinkFormatSpec);
2269 static assert(hasToString!(L, char) == none);
2270 static if (hasPreviewIn)
2271 {
2272 static assert(hasToString!(M, char) == inCharSinkFormatSpec);
2273 static assert(hasToString!(N, char) == inCharSinkFormatString);
2274 static assert(hasToString!(O, char) == inCharSink);
2275 }
2276
2277 // https://issues.dlang.org/show_bug.cgi?id=22873
2278 static assert(hasToString!(inout(A), char) == customPutWriter);
2279 static assert(hasToString!(inout(B), char) == constCharSinkFormatSpec);
2280 static assert(hasToString!(inout(C), char) == constCharSinkFormatString);
2281 static assert(hasToString!(inout(D), char) == constCharSink);
2282 static assert(hasToString!(inout(E), char) == hasSomeToString);
2283 static assert(hasToString!(inout(F), char) == customPutWriterFormatSpec);
2284 static assert(hasToString!(inout(G), char) == customPutWriter);
2285 static assert(hasToString!(inout(H), char) == customPutWriterFormatSpec);
2286 static assert(hasToString!(inout(I), char) == customPutWriterFormatSpec);
2287 static assert(hasToString!(inout(J), char) == hasSomeToString);
2288 static assert(hasToString!(inout(K), char) == constCharSinkFormatSpec);
2289 static assert(hasToString!(inout(L), char) == none);
2290 static if (hasPreviewIn)
2291 {
2292 static assert(hasToString!(inout(M), char) == inCharSinkFormatSpec);
2293 static assert(hasToString!(inout(N), char) == inCharSinkFormatString);
2294 static assert(hasToString!(inout(O), char) == inCharSink);
2295 }
2296 }
2297}
2298
2174// object formatting with toString 2299// object formatting with toString
2175private void formatObject(Writer, T, Char)(ref Writer w, ref T val, scope const ref FormatSpec!Char f) 2300private void formatObject(Writer, T, Char)(ref Writer w, ref T val, scope const ref FormatSpec!Char f)
2176if (hasToString!(T, Char)) 2301if (hasToString!(T, Char))
diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d
index 6c9e9ae6004..76d68f6cff6 100644
--- a/libphobos/src/std/format/package.d
+++ b/libphobos/src/std/format/package.d
@@ -1589,7 +1589,7 @@ char[] sformat(alias fmt, Args...)(char[] buf, Args args)
1589if (isSomeString!(typeof(fmt))) 1589if (isSomeString!(typeof(fmt)))
1590{ 1590{
1591 alias e = checkFormatException!(fmt, Args); 1591 alias e = checkFormatException!(fmt, Args);
1592 static assert(!e, e.msg); 1592 static assert(!e, e);
1593 return .sformat(buf, fmt, args); 1593 return .sformat(buf, fmt, args);
1594} 1594}
1595 1595
diff --git a/libphobos/src/std/format/read.d b/libphobos/src/std/format/read.d
index b3d8d1f6c3f..144fa8c0d65 100644
--- a/libphobos/src/std/format/read.d
+++ b/libphobos/src/std/format/read.d
@@ -305,7 +305,7 @@ if (isSomeString!(typeof(fmt)))
305 import std.format : checkFormatException; 305 import std.format : checkFormatException;
306 306
307 alias e = checkFormatException!(fmt, Args); 307 alias e = checkFormatException!(fmt, Args);
308 static assert(!e, e.msg); 308 static assert(!e, e);
309 return .formattedRead(r, fmt, args); 309 return .formattedRead(r, fmt, args);
310} 310}
311 311
diff --git a/libphobos/src/std/format/write.d b/libphobos/src/std/format/write.d
index e67d95ccc23..cbab51217ac 100644
--- a/libphobos/src/std/format/write.d
+++ b/libphobos/src/std/format/write.d
@@ -677,7 +677,7 @@ if (isSomeString!(typeof(fmt)))
677 import std.format : checkFormatException; 677 import std.format : checkFormatException;
678 678
679 alias e = checkFormatException!(fmt, Args); 679 alias e = checkFormatException!(fmt, Args);
680 static assert(!e, e.msg); 680 static assert(!e, e);
681 return .formattedWrite(w, fmt, args); 681 return .formattedWrite(w, fmt, args);
682} 682}
683 683
diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d
index 90b0f91ecce..70aaee39aeb 100644
--- a/libphobos/src/std/functional.d
+++ b/libphobos/src/std/functional.d
@@ -65,6 +65,7 @@ module std.functional;
65 65
66import std.meta : AliasSeq, Reverse; 66import std.meta : AliasSeq, Reverse;
67import std.traits : isCallable, Parameters; 67import std.traits : isCallable, Parameters;
68import std.conv : toCtString;
68 69
69import std.internal.attributes : betterC; 70import std.internal.attributes : betterC;
70 71
@@ -1848,17 +1849,6 @@ if (isCallable!(F))
1848 } 1849 }
1849} 1850}
1850 1851
1851// Converts an unsigned integer to a compile-time string constant.
1852private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length];
1853
1854// Check that .stringof does what we expect, since it's not guaranteed by the
1855// language spec.
1856@safe unittest
1857{
1858 assert(toCtString!0 == "0");
1859 assert(toCtString!123456 == "123456");
1860}
1861
1862/** 1852/**
1863 * Passes the fields of a struct as arguments to a function. 1853 * Passes the fields of a struct as arguments to a function.
1864 * 1854 *
diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d
index 89def0f02f5..c6e746a0beb 100644
--- a/libphobos/src/std/json.d
+++ b/libphobos/src/std/json.d
@@ -321,7 +321,7 @@ struct JSONValue
321 (*a)[0] = "world"; // segmentation fault 321 (*a)[0] = "world"; // segmentation fault
322 --- 322 ---
323 */ 323 */
324 @property ref inout(JSONValue[]) array() return scope inout pure @system 324 @property ref inout(JSONValue[]) array() scope return inout pure @system
325 { 325 {
326 enforce!JSONException(type == JSONType.array, 326 enforce!JSONException(type == JSONType.array,
327 "JSONValue is not an array"); 327 "JSONValue is not an array");
diff --git a/libphobos/src/std/outbuffer.d b/libphobos/src/std/outbuffer.d
index 9590238c7f9..c434481dda3 100644
--- a/libphobos/src/std/outbuffer.d
+++ b/libphobos/src/std/outbuffer.d
@@ -331,7 +331,7 @@ class OutBuffer
331 import std.format : checkFormatException; 331 import std.format : checkFormatException;
332 332
333 alias e = checkFormatException!(fmt, A); 333 alias e = checkFormatException!(fmt, A);
334 static assert(!e, e.msg); 334 static assert(!e, e);
335 return this.writef(fmt, args); 335 return this.writef(fmt, args);
336 } 336 }
337 337
@@ -377,7 +377,7 @@ class OutBuffer
377 import std.format : checkFormatException; 377 import std.format : checkFormatException;
378 378
379 alias e = checkFormatException!(fmt, A); 379 alias e = checkFormatException!(fmt, A);
380 static assert(!e, e.msg); 380 static assert(!e, e);
381 return this.writefln(fmt, args); 381 return this.writefln(fmt, args);
382 } 382 }
383 383
diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d
index 971cfa07e2b..2c976388d10 100644
--- a/libphobos/src/std/parallelism.d
+++ b/libphobos/src/std/parallelism.d
@@ -1039,6 +1039,11 @@ uint totalCPUsImpl() @nogc nothrow @trusted
1039 import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; 1039 import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
1040 return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); 1040 return cast(uint) sysconf(_SC_NPROCESSORS_ONLN);
1041 } 1041 }
1042 else version (Hurd)
1043 {
1044 import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
1045 return cast(uint) sysconf(_SC_NPROCESSORS_ONLN);
1046 }
1042 else 1047 else
1043 { 1048 {
1044 static assert(0, "Don't know how to get N CPUs on this OS."); 1049 static assert(0, "Don't know how to get N CPUs on this OS.");
diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d
index 2c68f36b4e7..28bfb049c2b 100644
--- a/libphobos/src/std/process.d
+++ b/libphobos/src/std/process.d
@@ -299,10 +299,9 @@ static:
299 } 299 }
300 else version (Windows) 300 else version (Windows)
301 { 301 {
302 import std.exception : enforce; 302 import std.windows.syserror : wenforce;
303 enforce( 303 wenforce(
304 SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()), 304 SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()),
305 sysErrorString(GetLastError())
306 ); 305 );
307 return value; 306 return value;
308 } 307 }
@@ -1330,7 +1329,7 @@ private Pid spawnProcessWin(scope const(char)[] commandLine,
1330 { 1329 {
1331 throw new StdioException( 1330 throw new StdioException(
1332 "Failed to make "~which~" stream inheritable by child process (" 1331 "Failed to make "~which~" stream inheritable by child process ("
1333 ~sysErrorString(GetLastError()) ~ ')', 1332 ~generateSysErrorMsg() ~ ')',
1334 0); 1333 0);
1335 } 1334 }
1336 } 1335 }
@@ -2775,7 +2774,7 @@ Pipe pipe() @trusted //TODO: @safe
2775 if (!CreatePipe(&readHandle, &writeHandle, null, 0)) 2774 if (!CreatePipe(&readHandle, &writeHandle, null, 0))
2776 { 2775 {
2777 throw new StdioException( 2776 throw new StdioException(
2778 "Error creating pipe (" ~ sysErrorString(GetLastError()) ~ ')', 2777 "Error creating pipe (" ~ generateSysErrorMsg() ~ ')',
2779 0); 2778 0);
2780 } 2779 }
2781 2780
@@ -3475,7 +3474,7 @@ class ProcessException : Exception
3475 string file = __FILE__, 3474 string file = __FILE__,
3476 size_t line = __LINE__) 3475 size_t line = __LINE__)
3477 { 3476 {
3478 auto lastMsg = sysErrorString(GetLastError()); 3477 auto lastMsg = generateSysErrorMsg();
3479 auto msg = customMsg.empty ? lastMsg 3478 auto msg = customMsg.empty ? lastMsg
3480 : customMsg ~ " (" ~ lastMsg ~ ')'; 3479 : customMsg ~ " (" ~ lastMsg ~ ')';
3481 return new ProcessException(msg, file, line); 3480 return new ProcessException(msg, file, line);
diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d
index cd2323281d3..915159f180f 100644
--- a/libphobos/src/std/socket.d
+++ b/libphobos/src/std/socket.d
@@ -169,7 +169,7 @@ string formatSocketError(int err) @trusted
169 else 169 else
170 version (Windows) 170 version (Windows)
171 { 171 {
172 return sysErrorString(err); 172 return generateSysErrorMsg(err);
173 } 173 }
174 else 174 else
175 return "Socket error " ~ to!string(err); 175 return "Socket error " ~ to!string(err);
@@ -842,7 +842,7 @@ private string formatGaiError(int err) @trusted
842{ 842{
843 version (Windows) 843 version (Windows)
844 { 844 {
845 return sysErrorString(err); 845 return generateSysErrorMsg(err);
846 } 846 }
847 else 847 else
848 { 848 {
diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d
index bc2d3fe4210..1bde73d628d 100644
--- a/libphobos/src/std/stdio.d
+++ b/libphobos/src/std/stdio.d
@@ -1458,6 +1458,7 @@ Throws: `Exception` if the file is not opened.
1458 { 1458 {
1459 import core.sys.windows.winbase : OVERLAPPED; 1459 import core.sys.windows.winbase : OVERLAPPED;
1460 import core.sys.windows.winnt : BOOL, ULARGE_INTEGER; 1460 import core.sys.windows.winnt : BOOL, ULARGE_INTEGER;
1461 import std.windows.syserror : wenforce;
1461 1462
1462 private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, 1463 private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
1463 Flags flags) 1464 Flags flags)
@@ -1474,15 +1475,6 @@ Throws: `Exception` if the file is not opened.
1474 return F(windowsHandle, flags, 0, liLength.LowPart, 1475 return F(windowsHandle, flags, 0, liLength.LowPart,
1475 liLength.HighPart, &overlapped); 1476 liLength.HighPart, &overlapped);
1476 } 1477 }
1477
1478 private static T wenforce(T)(T cond, lazy string str)
1479 {
1480 import core.sys.windows.winbase : GetLastError;
1481 import std.windows.syserror : sysErrorString;
1482
1483 if (cond) return cond;
1484 throw new Exception(str ~ ": " ~ sysErrorString(GetLastError()));
1485 }
1486 } 1478 }
1487 version (Posix) 1479 version (Posix)
1488 { 1480 {
@@ -1783,7 +1775,7 @@ Throws: `Exception` if the file is not opened.
1783 import std.format : checkFormatException; 1775 import std.format : checkFormatException;
1784 1776
1785 alias e = checkFormatException!(fmt, A); 1777 alias e = checkFormatException!(fmt, A);
1786 static assert(!e, e.msg); 1778 static assert(!e, e);
1787 return this.writef(fmt, args); 1779 return this.writef(fmt, args);
1788 } 1780 }
1789 1781
@@ -1802,7 +1794,7 @@ Throws: `Exception` if the file is not opened.
1802 import std.format : checkFormatException; 1794 import std.format : checkFormatException;
1803 1795
1804 alias e = checkFormatException!(fmt, A); 1796 alias e = checkFormatException!(fmt, A);
1805 static assert(!e, e.msg); 1797 static assert(!e, e);
1806 return this.writefln(fmt, args); 1798 return this.writefln(fmt, args);
1807 } 1799 }
1808 1800
@@ -2151,7 +2143,7 @@ $(CONSOLE
2151 import std.format : checkFormatException; 2143 import std.format : checkFormatException;
2152 2144
2153 alias e = checkFormatException!(format, Data); 2145 alias e = checkFormatException!(format, Data);
2154 static assert(!e, e.msg); 2146 static assert(!e, e);
2155 return this.readf(format, data); 2147 return this.readf(format, data);
2156 } 2148 }
2157 2149
@@ -4388,7 +4380,7 @@ if (isSomeString!(typeof(fmt)))
4388 import std.format : checkFormatException; 4380 import std.format : checkFormatException;
4389 4381
4390 alias e = checkFormatException!(fmt, A); 4382 alias e = checkFormatException!(fmt, A);
4391 static assert(!e, e.msg); 4383 static assert(!e, e);
4392 return .writef(fmt, args); 4384 return .writef(fmt, args);
4393} 4385}
4394 4386
@@ -4429,7 +4421,7 @@ if (isSomeString!(typeof(fmt)))
4429 import std.format : checkFormatException; 4421 import std.format : checkFormatException;
4430 4422
4431 alias e = checkFormatException!(fmt, A); 4423 alias e = checkFormatException!(fmt, A);
4432 static assert(!e, e.msg); 4424 static assert(!e, e);
4433 return .writefln(fmt, args); 4425 return .writefln(fmt, args);
4434} 4426}
4435 4427
@@ -4510,7 +4502,7 @@ if (isSomeString!(typeof(format)))
4510 import std.format : checkFormatException; 4502 import std.format : checkFormatException;
4511 4503
4512 alias e = checkFormatException!(format, A); 4504 alias e = checkFormatException!(format, A);
4513 static assert(!e, e.msg); 4505 static assert(!e, e);
4514 return .readf(format, args); 4506 return .readf(format, args);
4515} 4507}
4516 4508
diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d
index f4f42167044..dac531ff1ae 100644
--- a/libphobos/src/std/sumtype.d
+++ b/libphobos/src/std/sumtype.d
@@ -237,21 +237,11 @@ import std.traits : ConstOf, ImmutableOf, InoutOf, TemplateArgsOf;
237import std.traits : CommonType, DeducedParameterType; 237import std.traits : CommonType, DeducedParameterType;
238import std.typecons : ReplaceTypeUnless; 238import std.typecons : ReplaceTypeUnless;
239import std.typecons : Flag; 239import std.typecons : Flag;
240import std.conv : toCtString;
240 241
241/// Placeholder used to refer to the enclosing [SumType]. 242/// Placeholder used to refer to the enclosing [SumType].
242struct This {} 243struct This {}
243 244
244// Converts an unsigned integer to a compile-time string constant.
245private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length];
246
247// Check that .stringof does what we expect, since it's not guaranteed by the
248// language spec.
249@safe unittest
250{
251 assert(toCtString!0 == "0");
252 assert(toCtString!123456 == "123456");
253}
254
255// True if a variable of type T can appear on the lhs of an assignment 245// True if a variable of type T can appear on the lhs of an assignment
256private enum isAssignableTo(T) = 246private enum isAssignableTo(T) =
257 isAssignable!T || (!isCopyable!T && isRvalueAssignable!T); 247 isAssignable!T || (!isCopyable!T && isRvalueAssignable!T);
diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d
index ea8f8bd0545..1ee7faaee7b 100644
--- a/libphobos/src/std/typecons.d
+++ b/libphobos/src/std/typecons.d
@@ -3109,6 +3109,64 @@ struct Nullable(T)
3109 { 3109 {
3110 return isNull ? fallback : _value.payload; 3110 return isNull ? fallback : _value.payload;
3111 } 3111 }
3112
3113 /// $(MREF_ALTTEXT Range interface, std, range, primitives) functions.
3114 alias empty = isNull;
3115
3116 /// ditto
3117 alias popFront = nullify;
3118
3119 /// ditto
3120 alias popBack = nullify;
3121
3122 /// ditto
3123 @property ref inout(T) front() inout @safe pure nothrow
3124 {
3125 return get();
3126 }
3127
3128 /// ditto
3129 alias back = front;
3130
3131 /// ditto
3132 @property inout(typeof(this)) save() inout
3133 {
3134 return this;
3135 }
3136
3137 /// ditto
3138 inout(typeof(this)) opIndex() inout
3139 {
3140 return this;
3141 }
3142
3143 /// ditto
3144 inout(typeof(this)) opIndex(size_t[2] dim) inout
3145 in (dim[0] <= length && dim[1] <= length && dim[1] >= dim[0])
3146 {
3147 return (dim[0] == 0 && dim[1] == 1) ? this : this.init;
3148 }
3149 /// ditto
3150 size_t[2] opSlice(size_t dim : 0)(size_t from, size_t to) const
3151 {
3152 return [from, to];
3153 }
3154
3155 /// ditto
3156 @property size_t length() const @safe pure nothrow
3157 {
3158 return !empty;
3159 }
3160
3161 /// ditto
3162 alias opDollar(size_t dim : 0) = length;
3163
3164 /// ditto
3165 ref inout(T) opIndex(size_t index) inout @safe pure nothrow
3166 in (index < length)
3167 {
3168 return get();
3169 }
3112} 3170}
3113 3171
3114/// ditto 3172/// ditto
@@ -3162,6 +3220,23 @@ auto nullable(T)(T t)
3162 assert(a.isNull); 3220 assert(a.isNull);
3163 assertThrown!Throwable(a.get); 3221 assertThrown!Throwable(a.get);
3164} 3222}
3223///
3224@safe unittest
3225{
3226 import std.algorithm.iteration : each, joiner;
3227 Nullable!int a = 42;
3228 Nullable!int b;
3229 // Add each value to an array
3230 int[] arr;
3231 a.each!((n) => arr ~= n);
3232 assert(arr == [42]);
3233 b.each!((n) => arr ~= n);
3234 assert(arr == [42]);
3235 // Take first value from an array of Nullables
3236 Nullable!int[] c = new Nullable!int[](10);
3237 c[7] = Nullable!int(42);
3238 assert(c.joiner.front == 42);
3239}
3165@safe unittest 3240@safe unittest
3166{ 3241{
3167 auto k = Nullable!int(74); 3242 auto k = Nullable!int(74);
@@ -3638,6 +3713,42 @@ auto nullable(T)(T t)
3638 a = b = c = nullable(5); 3713 a = b = c = nullable(5);
3639} 3714}
3640 3715
3716// https://issues.dlang.org/show_bug.cgi?id=18374
3717@safe pure nothrow unittest
3718{
3719 import std.algorithm.comparison : equal;
3720 import std.range : only, takeNone;
3721 import std.range.primitives : hasAssignableElements, hasLength,
3722 hasLvalueElements, hasSlicing, hasSwappableElements,
3723 isRandomAccessRange;
3724 Nullable!int a = 42;
3725 assert(!a.empty);
3726 assert(a.front == 42);
3727 assert(a.back == 42);
3728 assert(a[0] == 42);
3729 assert(a.equal(only(42)));
3730 assert(a[0 .. $].equal(only(42)));
3731 a[0] = 43;
3732 assert(a.equal(only(43)));
3733 --a[0];
3734 assert(a.equal(only(42)));
3735 Nullable!int b;
3736 assert(b.empty);
3737 assert(b.equal(takeNone(b)));
3738 Nullable!int c = a.save();
3739 assert(!c.empty);
3740 c.popFront();
3741 assert(!a.empty);
3742 assert(c.empty);
3743
3744 assert(isRandomAccessRange!(Nullable!int));
3745 assert(hasLength!(Nullable!int));
3746 assert(hasSlicing!(Nullable!int));
3747 assert(hasAssignableElements!(Nullable!int));
3748 assert(hasSwappableElements!(Nullable!int));
3749 assert(hasLvalueElements!(Nullable!int));
3750}
3751
3641/** 3752/**
3642Just like `Nullable!T`, except that the null state is defined as a 3753Just like `Nullable!T`, except that the null state is defined as a
3643particular value. For example, $(D Nullable!(uint, uint.max)) is an 3754particular value. For example, $(D Nullable!(uint, uint.max)) is an
@@ -8793,19 +8904,6 @@ private:
8793 enum isBaseEnumType(T) = is(E == T); 8904 enum isBaseEnumType(T) = is(E == T);
8794 alias Base = OriginalType!E; 8905 alias Base = OriginalType!E;
8795 Base mValue; 8906 Base mValue;
8796 static struct Negation
8797 {
8798 @safe @nogc pure nothrow:
8799 private:
8800 Base mValue;
8801
8802 // Prevent non-copy construction outside the module.
8803 @disable this();
8804 this(Base value)
8805 {
8806 mValue = value;
8807 }
8808 }
8809 8907
8810public: 8908public:
8811 this(E flag) 8909 this(E flag)
@@ -8830,10 +8928,10 @@ public:
8830 return mValue; 8928 return mValue;
8831 } 8929 }
8832 8930
8833 Negation opUnary(string op)() const 8931 auto opUnary(string op)() const
8834 if (op == "~") 8932 if (op == "~")
8835 { 8933 {
8836 return Negation(~mValue); 8934 return BitFlags(cast(E) cast(Base) ~mValue);
8837 } 8935 }
8838 8936
8839 auto ref opAssign(T...)(T flags) 8937 auto ref opAssign(T...)(T flags)
@@ -8877,12 +8975,6 @@ public:
8877 return this; 8975 return this;
8878 } 8976 }
8879 8977
8880 auto ref opOpAssign(string op: "&")(Negation negatedFlags)
8881 {
8882 mValue &= negatedFlags.mValue;
8883 return this;
8884 }
8885
8886 auto opBinary(string op)(BitFlags flags) const 8978 auto opBinary(string op)(BitFlags flags) const
8887 if (op == "|" || op == "&") 8979 if (op == "|" || op == "&")
8888 { 8980 {
@@ -8899,13 +8991,6 @@ public:
8899 return result; 8991 return result;
8900 } 8992 }
8901 8993
8902 auto opBinary(string op: "&")(Negation negatedFlags) const
8903 {
8904 BitFlags result = this;
8905 result.opOpAssign!op(negatedFlags);
8906 return result;
8907 }
8908
8909 auto opBinaryRight(string op)(E flag) const 8994 auto opBinaryRight(string op)(E flag) const
8910 if (op == "|" || op == "&") 8995 if (op == "|" || op == "&")
8911 { 8996 {
@@ -9104,6 +9189,34 @@ public:
9104 assert(flags.A && !flags.B && !flags.C); 9189 assert(flags.A && !flags.B && !flags.C);
9105} 9190}
9106 9191
9192// Negation of BitFlags should work with any base type.
9193// Double-negation of BitFlags should work.
9194@safe @nogc pure nothrow unittest
9195{
9196 static foreach (alias Base; AliasSeq!(
9197 byte,
9198 ubyte,
9199 short,
9200 ushort,
9201 int,
9202 uint,
9203 long,
9204 ulong,
9205 ))
9206 {{
9207 enum Enum : Base
9208 {
9209 A = 1 << 0,
9210 B = 1 << 1,
9211 C = 1 << 2,
9212 }
9213
9214 auto flags = BitFlags!Enum(Enum.A);
9215
9216 assert(flags == ~~flags);
9217 }}
9218}
9219
9107private enum false_(T) = false; 9220private enum false_(T) = false;
9108 9221
9109// ReplaceType 9222// ReplaceType
diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d
index eeeda721813..98735ac1a88 100644
--- a/libphobos/src/std/uni/package.d
+++ b/libphobos/src/std/uni/package.d
@@ -9834,25 +9834,29 @@ dchar toLower(dchar c)
9834 Returns: 9834 Returns:
9835 An array with the same element type as `s`. 9835 An array with the same element type as `s`.
9836+/ 9836+/
9837ElementEncodingType!S[] toLower(S)(S s) 9837ElementEncodingType!S[] toLower(S)(return scope S s) @trusted
9838if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) 9838if (isSomeString!S)
9839{ 9839{
9840 static import std.ascii; 9840 static import std.ascii;
9841 return toCase!(LowerTriple, std.ascii.toLower)(s);
9842}
9841 9843
9842 static if (isSomeString!S) 9844/// ditto
9843 return () @trusted { return toCase!(LowerTriple, std.ascii.toLower)(s); } (); 9845ElementEncodingType!S[] toLower(S)(S s)
9844 else 9846if (!isSomeString!S && (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S)))
9845 return toCase!(LowerTriple, std.ascii.toLower)(s); 9847{
9848 static import std.ascii;
9849 return toCase!(LowerTriple, std.ascii.toLower)(s);
9846} 9850}
9847 9851
9848// overloads for the most common cases to reduce compile time 9852// overloads for the most common cases to reduce compile time
9849@safe pure /*TODO nothrow*/ 9853@safe pure /*TODO nothrow*/
9850{ 9854{
9851 string toLower(string s) 9855 string toLower(return scope string s)
9852 { return toLower!string(s); } 9856 { return toLower!string(s); }
9853 wstring toLower(wstring s) 9857 wstring toLower(return scope wstring s)
9854 { return toLower!wstring(s); } 9858 { return toLower!wstring(s); }
9855 dstring toLower(dstring s) 9859 dstring toLower(return scope dstring s)
9856 { return toLower!dstring(s); } 9860 { return toLower!dstring(s); }
9857 9861
9858 @safe unittest 9862 @safe unittest
@@ -10038,25 +10042,29 @@ dchar toUpper(dchar c)
10038 Returns: 10042 Returns:
10039 An new array with the same element type as `s`. 10043 An new array with the same element type as `s`.
10040+/ 10044+/
10041ElementEncodingType!S[] toUpper(S)(S s) 10045ElementEncodingType!S[] toUpper(S)(return scope S s) @trusted
10042if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) 10046if (isSomeString!S)
10043{ 10047{
10044 static import std.ascii; 10048 static import std.ascii;
10049 return toCase!(UpperTriple, std.ascii.toUpper)(s);
10050}
10045 10051
10046 static if (isSomeString!S) 10052/// ditto
10047 return () @trusted { return toCase!(UpperTriple, std.ascii.toUpper)(s); } (); 10053ElementEncodingType!S[] toUpper(S)(S s)
10048 else 10054if (!isSomeString!S && (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S)))
10049 return toCase!(UpperTriple, std.ascii.toUpper)(s); 10055{
10056 static import std.ascii;
10057 return toCase!(UpperTriple, std.ascii.toUpper)(s);
10050} 10058}
10051 10059
10052// overloads for the most common cases to reduce compile time 10060// overloads for the most common cases to reduce compile time
10053@safe pure /*TODO nothrow*/ 10061@safe pure /*TODO nothrow*/
10054{ 10062{
10055 string toUpper(string s) 10063 string toUpper(return scope string s)
10056 { return toUpper!string(s); } 10064 { return toUpper!string(s); }
10057 wstring toUpper(wstring s) 10065 wstring toUpper(return scope wstring s)
10058 { return toUpper!wstring(s); } 10066 { return toUpper!wstring(s); }
10059 dstring toUpper(dstring s) 10067 dstring toUpper(return scope dstring s)
10060 { return toUpper!dstring(s); } 10068 { return toUpper!dstring(s); }
10061 10069
10062 @safe unittest 10070 @safe unittest
diff --git a/libphobos/src/std/windows/charset.d b/libphobos/src/std/windows/charset.d
index 69626b553fa..ce33616810c 100644
--- a/libphobos/src/std/windows/charset.d
+++ b/libphobos/src/std/windows/charset.d
@@ -76,12 +76,7 @@ const(char)* toMBSz(scope const(char)[] s, uint codePage = 0)
76 to!int(result.length), null, null); 76 to!int(result.length), null, null);
77 } 77 }
78 78
79 if (!readLen || readLen != result.length) 79 wenforce(readLen && readLen == result.length, "Couldn't convert string");
80 {
81 throw new Exception("Couldn't convert string: " ~
82 sysErrorString(GetLastError()));
83 }
84
85 return result.ptr; 80 return result.ptr;
86 } 81 }
87 } 82 }
@@ -107,16 +102,10 @@ string fromMBSz(return scope immutable(char)* s, int codePage = 0)
107 to!int(result.length)); 102 to!int(result.length));
108 } 103 }
109 104
110 if (!readLen || readLen != result.length) 105 wenforce(readLen && readLen == result.length, "Couldn't convert string");
111 {
112 throw new Exception("Couldn't convert string: " ~
113 sysErrorString(GetLastError()));
114 }
115 106
116 return result[0 .. result.length-1].to!string; // omit trailing null 107 return result[0 .. result.length-1].to!string; // omit trailing null
117 } 108 }
118 } 109 }
119 return s[0 .. c-s]; // string is ASCII, no conversion necessary 110 return s[0 .. c-s]; // string is ASCII, no conversion necessary
120} 111}
121
122
diff --git a/libphobos/src/std/windows/syserror.d b/libphobos/src/std/windows/syserror.d
index 94f8ee59d50..3d8c5e71cec 100644
--- a/libphobos/src/std/windows/syserror.d
+++ b/libphobos/src/std/windows/syserror.d
@@ -43,7 +43,7 @@ version (StdDdoc)
43 { 43 {
44 private alias DWORD = int; 44 private alias DWORD = int;
45 final @property DWORD code(); /// `GetLastError`'s return value. 45 final @property DWORD code(); /// `GetLastError`'s return value.
46 this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted; 46 this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted;
47 } 47 }
48 48
49 /++ 49 /++
@@ -66,9 +66,9 @@ else:
66version (Windows): 66version (Windows):
67 67
68import core.sys.windows.winbase, core.sys.windows.winnt; 68import core.sys.windows.winbase, core.sys.windows.winnt;
69import std.array : appender; 69import std.array : appender, Appender;
70import std.conv : to; 70import std.conv : to, toTextRange, text;
71import std.format.write : formattedWrite; 71import std.exception;
72import std.windows.charset; 72import std.windows.charset;
73 73
74string sysErrorString( 74string sysErrorString(
@@ -79,16 +79,25 @@ string sysErrorString(
79{ 79{
80 auto buf = appender!string(); 80 auto buf = appender!string();
81 81
82 if (!putSysError(errCode, buf, MAKELANGID(langId, subLangId))) 82 wenforce(
83 { 83 // Ignore unlikely UTF decoding errors, always report the actual error (`errCode`)
84 throw new Exception( 84 putSysError(errCode, buf, MAKELANGID(langId, subLangId)).ifThrown(false),
85 "failed getting error string for WinAPI error code: " ~ 85 text("Could not fetch error string for WinAPI code ", errCode)
86 sysErrorString(GetLastError())); 86 );
87 }
88 87
89 return buf.data; 88 return buf.data;
90} 89}
91 90
91@safe unittest
92{
93 import std.algorithm.searching;
94
95 assert(sysErrorString(ERROR_PATH_NOT_FOUND) !is null);
96
97 const msg = collectExceptionMsg!WindowsException(sysErrorString(DWORD.max));
98 assert(msg.startsWith(`Could not fetch error string for WinAPI code 4294967295: `));
99}
100
92bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) 101bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0)
93{ 102{
94 wchar *lpMsgBuf = null; 103 wchar *lpMsgBuf = null;
@@ -114,7 +123,6 @@ bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0)
114 return false; 123 return false;
115} 124}
116 125
117
118class WindowsException : Exception 126class WindowsException : Exception
119{ 127{
120 import core.sys.windows.windef : DWORD; 128 import core.sys.windows.windef : DWORD;
@@ -122,11 +130,11 @@ class WindowsException : Exception
122 final @property DWORD code() { return _code; } /// `GetLastError`'s return value. 130 final @property DWORD code() { return _code; } /// `GetLastError`'s return value.
123 private DWORD _code; 131 private DWORD _code;
124 132
125 this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted 133 this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted
126 { 134 {
127 _code = code; 135 _code = code;
128 136
129 auto buf = appender!string(); 137 auto buf = appender!(char[]);
130 138
131 if (str != null) 139 if (str != null)
132 { 140 {
@@ -135,18 +143,43 @@ class WindowsException : Exception
135 buf.put(": "); 143 buf.put(": ");
136 } 144 }
137 145
138 if (code) 146 if (code && writeErrorMessage(code, buf))
139 { 147 {
140 auto success = putSysError(code, buf); 148 buf.put(" (error ");
141 formattedWrite(buf, success ? " (error %d)" : "Error %d", code); 149 toTextRange(code, buf);
150 buf.put(')');
142 } 151 }
143 152
144 super(buf.data, file, line); 153 super(cast(immutable) buf.data, file, line);
145 } 154 }
146} 155}
147 156
157/// Writes the error string associated to `code` into `buf`.
158/// Writes `Error <code>` when the error message lookup fails
159private bool writeErrorMessage(DWORD code, ref Appender!(char[]) buf) nothrow
160{
161 bool success;
162 try
163 {
164 // Reset the buffer to undo partial changes
165 const len = buf[].length;
166 scope (failure) buf.shrinkTo(len);
148 167
149T wenforce(T, S)(T value, lazy S msg = null, 168 success = putSysError(code, buf);
169 }
170 catch (Exception) {}
171
172 // Write the error code instead if we couldn't find the string
173 if (!success)
174 {
175 buf.put("Error ");
176 toTextRange(code, buf);
177 }
178
179 return success;
180}
181
182T wenforce(T, S = string)(T value, lazy S msg = null,
150string file = __FILE__, size_t line = __LINE__) 183string file = __FILE__, size_t line = __LINE__)
151if (isSomeString!S) 184if (isSomeString!S)
152{ 185{
@@ -177,11 +210,9 @@ T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file
177 throw new WindowsException(GetLastError(), names, file, line); 210 throw new WindowsException(GetLastError(), names, file, line);
178} 211}
179 212
180version (Windows)
181@system unittest 213@system unittest
182{ 214{
183 import std.algorithm.searching : startsWith, endsWith; 215 import std.algorithm.searching : startsWith, endsWith;
184 import std.exception;
185 import std.string; 216 import std.string;
186 217
187 auto e = collectException!WindowsException( 218 auto e = collectException!WindowsException(
@@ -199,3 +230,29 @@ version (Windows)
199 e = new WindowsException(0, "Test"); 230 e = new WindowsException(0, "Test");
200 assert(e.msg == "Test"); 231 assert(e.msg == "Test");
201} 232}
233
234@safe nothrow unittest
235{
236 import std.algorithm.searching : endsWith;
237
238 auto e = new WindowsException(ERROR_FILE_NOT_FOUND);
239 assert(e.msg.endsWith("(error 2)"));
240
241 e = new WindowsException(DWORD.max);
242 assert(e.msg == "Error 4294967295");
243}
244
245/// Tries to translate an error code from the Windows API to the corresponding
246/// error message. Returns `Error <code>` on failure
247package (std) string generateSysErrorMsg(DWORD errCode = GetLastError()) nothrow @trusted
248{
249 auto buf = appender!(char[]);
250 cast(void) writeErrorMessage(errCode, buf);
251 return cast(immutable) buf[];
252}
253
254nothrow @safe unittest
255{
256 assert(generateSysErrorMsg(ERROR_PATH_NOT_FOUND) !is null);
257 assert(generateSysErrorMsg(DWORD.max) == "Error 4294967295");
258}
diff --git a/libphobos/testsuite/libphobos.exceptions/message_with_null.d b/libphobos/testsuite/libphobos.exceptions/message_with_null.d
new file mode 100644
index 00000000000..64092f0c5cd
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/message_with_null.d
@@ -0,0 +1,8 @@
1// { dg-shouldfail " world!" }
2// { dg-output "object.Exception@.*: hello.*world!" }
3module message_with_null;
4
5void main()
6{
7 throw new Exception("hello\0 world!");
8}