diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2022-03-29 16:57:10 +0200 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2022-04-02 23:56:52 +0200 |
commit | 235d5a96cb8dad0b4c427602346fcf966a4ec914 (patch) | |
tree | ca19c774a19ad923e5d6f09d43ee8d89c275a96e /libphobos | |
parent | be07535d0f43390b8906826cc119473dea514b54 (diff) | |
download | gcc-235d5a96cb8dad0b4c427602346fcf966a4ec914.zip gcc-235d5a96cb8dad0b4c427602346fcf966a4ec914.tar.gz gcc-235d5a96cb8dad0b4c427602346fcf966a4ec914.tar.bz2 |
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')
36 files changed, 580 insertions, 205 deletions
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 77b6ad0..b1da32e 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -26b581670ef6e2643d74078f200d1cd21fa40e90 +c52e28b723ccfbe845a95e8e7b528e3cc0b9d790 The first line of this file holds the git revision number of the last merge done from the dlang/druntime repository. diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index ba64131..6ca4012 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -294,13 +294,13 @@ DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \ DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \ core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \ - core/sys/openbsd/pthread_np.d core/sys/openbsd/stdlib.d \ - core/sys/openbsd/string.d core/sys/openbsd/sys/cdefs.d \ - core/sys/openbsd/sys/elf.d core/sys/openbsd/sys/elf32.d \ - core/sys/openbsd/sys/elf64.d core/sys/openbsd/sys/elf_common.d \ - core/sys/openbsd/sys/link_elf.d core/sys/openbsd/sys/mman.d \ - core/sys/openbsd/sys/sysctl.d core/sys/openbsd/time.d \ - core/sys/openbsd/unistd.d + core/sys/openbsd/pthread_np.d core/sys/openbsd/pwd.d \ + core/sys/openbsd/stdlib.d core/sys/openbsd/string.d \ + core/sys/openbsd/sys/cdefs.d core/sys/openbsd/sys/elf.d \ + core/sys/openbsd/sys/elf32.d core/sys/openbsd/sys/elf64.d \ + core/sys/openbsd/sys/elf_common.d core/sys/openbsd/sys/link_elf.d \ + core/sys/openbsd/sys/mman.d core/sys/openbsd/sys/sysctl.d \ + core/sys/openbsd/time.d core/sys/openbsd/unistd.d DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \ core/sys/posix/arpa/inet.d core/sys/posix/config.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 1c64d35..f7f78d7 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 \ @DRUNTIME_OS_NETBSD_TRUE@am__objects_15 = $(am__objects_14) am__objects_16 = core/sys/openbsd/dlfcn.lo core/sys/openbsd/err.lo \ core/sys/openbsd/execinfo.lo core/sys/openbsd/pthread_np.lo \ - core/sys/openbsd/stdlib.lo core/sys/openbsd/string.lo \ - core/sys/openbsd/sys/cdefs.lo core/sys/openbsd/sys/elf.lo \ - core/sys/openbsd/sys/elf32.lo core/sys/openbsd/sys/elf64.lo \ + core/sys/openbsd/pwd.lo core/sys/openbsd/stdlib.lo \ + core/sys/openbsd/string.lo core/sys/openbsd/sys/cdefs.lo \ + core/sys/openbsd/sys/elf.lo core/sys/openbsd/sys/elf32.lo \ + core/sys/openbsd/sys/elf64.lo \ core/sys/openbsd/sys/elf_common.lo \ core/sys/openbsd/sys/link_elf.lo core/sys/openbsd/sys/mman.lo \ core/sys/openbsd/sys/sysctl.lo core/sys/openbsd/time.lo \ @@ -958,13 +959,13 @@ DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \ DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \ core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \ - core/sys/openbsd/pthread_np.d core/sys/openbsd/stdlib.d \ - core/sys/openbsd/string.d core/sys/openbsd/sys/cdefs.d \ - core/sys/openbsd/sys/elf.d core/sys/openbsd/sys/elf32.d \ - core/sys/openbsd/sys/elf64.d core/sys/openbsd/sys/elf_common.d \ - core/sys/openbsd/sys/link_elf.d core/sys/openbsd/sys/mman.d \ - core/sys/openbsd/sys/sysctl.d core/sys/openbsd/time.d \ - core/sys/openbsd/unistd.d + core/sys/openbsd/pthread_np.d core/sys/openbsd/pwd.d \ + core/sys/openbsd/stdlib.d core/sys/openbsd/string.d \ + core/sys/openbsd/sys/cdefs.d core/sys/openbsd/sys/elf.d \ + core/sys/openbsd/sys/elf32.d core/sys/openbsd/sys/elf64.d \ + core/sys/openbsd/sys/elf_common.d core/sys/openbsd/sys/link_elf.d \ + core/sys/openbsd/sys/mman.d core/sys/openbsd/sys/sysctl.d \ + core/sys/openbsd/time.d core/sys/openbsd/unistd.d DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \ 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) core/sys/openbsd/err.lo: core/sys/openbsd/$(am__dirstamp) core/sys/openbsd/execinfo.lo: core/sys/openbsd/$(am__dirstamp) core/sys/openbsd/pthread_np.lo: core/sys/openbsd/$(am__dirstamp) +core/sys/openbsd/pwd.lo: core/sys/openbsd/$(am__dirstamp) core/sys/openbsd/stdlib.lo: core/sys/openbsd/$(am__dirstamp) core/sys/openbsd/string.lo: core/sys/openbsd/$(am__dirstamp) core/sys/openbsd/sys/$(am__dirstamp): diff --git a/libphobos/libdruntime/core/atomic.d b/libphobos/libdruntime/core/atomic.d index e6a82e5..4af3fdf 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. in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") { // resolve implicit conversions - T arg1 = ifThis; + const T arg1 = ifThis; T arg2 = writeThis; static if (__traits(isFloating, T)) @@ -1276,4 +1276,13 @@ version (CoreUnittest) shared NoIndirections n; static assert(is(typeof(atomicLoad(n)) == NoIndirections)); } + + unittest // Issue 21631 + { + shared uint si1 = 45; + shared uint si2 = 38; + shared uint* psi = &si1; + + assert((&psi).cas(cast(const) psi, &si2)); + } } diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d index 930e0cd..cb8d433 100644 --- a/libphobos/libdruntime/core/demangle.d +++ b/libphobos/libdruntime/core/demangle.d @@ -1339,7 +1339,7 @@ pure @safe: TypeFunction: CallConvention FuncAttrs Arguments ArgClose Type */ - char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no ) return + char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no ) return scope { debug(trace) printf( "parseTypeFunction+\n" ); 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 e862f8e..4366da8 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 bloat. Params: - fromType = name of the type being cast from - fromSize = total size in bytes of the array being cast from - toType = name of the type being cast o - toSize = total size in bytes of the array being cast to + fromType = name of the type being cast from + fromSize = total size in bytes of the array being cast from + fromLength = length of array being cast from + toType = name of the type being cast to + toElemSize = element size of array being cast to */ -private void onArrayCastError()(string fromType, size_t fromSize, string toType, size_t toSize) @trusted +private void onArrayCastError()(string fromType, size_t fromSize, size_t fromLength, string toType, size_t toElemSize) @trusted { import core.internal.string : unsignedToTempString; import core.memory : pureMalloc; @@ -45,17 +46,22 @@ private void onArrayCastError()(string fromType, size_t fromSize, string toType, index += N; } - add("An array of size "); - auto s = unsignedToTempString(fromSize); + add("`"); + add(fromType); + add("[]` of length "); + auto s = unsignedToTempString(fromLength); add(s[]); - add(" does not align on an array of size "); - s = unsignedToTempString(toSize); + add(" cannot be cast to `"); + add(toType); + add("[]` as its length in bytes ("); + s = unsignedToTempString(fromSize); add(s[]); - add(", so `"); - add(fromType); - add("` cannot be cast to `"); + add(") is not a multiple of `"); add(toType); - add("`"); + add(".sizeof` ("); + s = unsignedToTempString(toElemSize); + add(s[]); + add(")."); msg[index] = '\0'; // null-termination // 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, /** The compiler lowers expressions of `cast(TTo[])TFrom[]` to -this implementation. +this implementation. Note that this does not detect alignment problems. Params: from = the array to reinterpret-cast @@ -79,7 +85,7 @@ TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted if ((fromSize % TTo.sizeof) != 0) { - onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof); + onArrayCastError(TFrom.stringof, fromSize, from.length, TTo.stringof, TTo.sizeof); } struct Array @@ -113,3 +119,26 @@ TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted foreach (v; s) assert(v == cast(short) 0xabab); } + +@system nothrow unittest +{ + string msg; + try + { + auto str = "hello"; + auto wstr = cast(wstring) str; + } + catch (Throwable t) + msg = t.msg; + + static immutable expected = "`immutable(char)[]` of length 5 cannot be cast to `immutable(wchar)[]` as " ~ + "its length in bytes (5) is not a multiple of `immutable(wchar).sizeof` (2)."; + + if (msg != expected) + { + import core.stdc.stdio; + printf("Expected: |%.*s|\n", cast(int) expected.length, expected.ptr); + printf("Actual : |%.*s|\n", cast(int) msg.length, msg.ptr); + assert(false); + } +} diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index aa51867..e29e426 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 else version (linux) { // clone() fits better as we don't want to do anything but scanning in the child process. - // no fork-handlera are called, so we can avoid deadlocks due to malloc locks. Probably related: + // no fork-handlers are called, so we can avoid deadlocks due to malloc locks. Probably related: // https://sourceware.org/bugzilla/show_bug.cgi?id=4737 import core.sys.linux.sched : clone; import core.sys.posix.signal : SIGCHLD; - enum CLONE_CHILD_CLEARTID = 0x00200000; /* Register exit futex and memory */ - const flags = CLONE_CHILD_CLEARTID | SIGCHLD; // child thread id not needed + const flags = SIGCHLD; // exit signal scope int delegate() scope dg = &child_mark; extern(C) static int wrap_delegate(void* arg) { auto dg = cast(int delegate() scope*)arg; return (*dg)(); } - char[256] stackbuf; // enough stack space for clone() to place some info for the child without stomping the parent stack + ubyte[256] stackbuf; // enough stack space for clone() to place some info for the child without stomping the parent stack auto stack = stackbuf.ptr + (isStackGrowingDown ? stackbuf.length : 0); auto pid = clone(&wrap_delegate, stack, flags, &dg); } @@ -2957,7 +2956,6 @@ struct Gcx auto pid = fork(); fork_needs_lock = true; } - assert(pid != -1); switch (pid) { case -1: // fork() failed, retry without forking @@ -3028,7 +3026,7 @@ struct Gcx //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr); version (COLLECT_FORK) - bool doFork = shouldFork; + alias doFork = shouldFork; else enum doFork = false; @@ -3083,6 +3081,7 @@ Lmark: final switch (forkResult) { case ChildStatus.error: + // fork() failed, retry without forking disableFork(); goto Lmark; case ChildStatus.running: diff --git a/libphobos/libdruntime/core/stdc/stdlib.d b/libphobos/libdruntime/core/stdc/stdlib.d index 92f8f70..dbe55a4 100644 --- a/libphobos/libdruntime/core/stdc/stdlib.d +++ b/libphobos/libdruntime/core/stdc/stdlib.d @@ -69,8 +69,8 @@ struct div_t /// struct ldiv_t { - int quot, - rem; + c_long quot, + rem; } /// diff --git a/libphobos/libdruntime/core/stdcpp/string.d b/libphobos/libdruntime/core/stdcpp/string.d index dfec1ec..defbd83 100644 --- a/libphobos/libdruntime/core/stdcpp/string.d +++ b/libphobos/libdruntime/core/stdcpp/string.d @@ -343,7 +343,7 @@ extern(D): /// inout(T)* data() inout @safe { return _Get_data()._Myptr; } /// - inout(T)[] as_array() return scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } + inout(T)[] as_array() scope return inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } @@ -1920,7 +1920,7 @@ extern(D): /// inout(T)* data() inout @safe { return __get_pointer(); } /// - inout(T)[] as_array() return scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; } + inout(T)[] as_array() scope return inout nothrow @trusted { return __get_pointer()[0 .. size()]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; } diff --git a/libphobos/libdruntime/core/sys/openbsd/pwd.d b/libphobos/libdruntime/core/sys/openbsd/pwd.d new file mode 100644 index 0000000..f6a35a8 --- /dev/null +++ b/libphobos/libdruntime/core/sys/openbsd/pwd.d @@ -0,0 +1,19 @@ +/** + * D header file for OpenBSD pwd.h. + * + * Copyright: Copyright © 2022, The D Language Foundation + * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. + * Authors: Brian Callahan + */ +module core.sys.openbsd.pwd; + +version (OpenBSD): +extern (C): +nothrow: +@nogc: + +public import core.sys.posix.pwd; +import core.sys.posix.sys.types : uid_t; + +passwd* getpwnam_shadow(scope const char*); +passwd* getpwuid_shadow(uid_t); diff --git a/libphobos/libdruntime/core/thread/context.d b/libphobos/libdruntime/core/thread/context.d index 1b3c0ca..e477269 100644 --- a/libphobos/libdruntime/core/thread/context.d +++ b/libphobos/libdruntime/core/thread/context.d @@ -6,7 +6,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak - * Source: $(DRUNTIMESRC core/thread/package.d) + * Source: $(DRUNTIMESRC core/thread/context.d) */ module core.thread.context; diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index 47b67f1..5ef2695 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) void sink(in char[] buf) scope nothrow { - fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr); + fwrite(buf.ptr, char.sizeof, buf.length, stderr); } formatThrowable(t, &sink); } diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 6eb555e..7306c10 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -a74fa63e6775d626850d8ebd854d9803c7ffb97d +99e9c1b7741e0f4e6f2a8c14883c4828d092701d 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/conv.d b/libphobos/src/std/conv.d index 06b3797..8f6c3bf 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -2850,7 +2850,7 @@ do static if (isNarrowString!Source) { import std.string : representation; - auto s = source.representation; + scope s = source.representation; } else { @@ -2898,7 +2898,7 @@ do } static if (isNarrowString!Source) - source = cast(Source) s; + source = source[$ - s.length .. $]; static if (doCount) { @@ -3105,7 +3105,7 @@ if (isSomeString!Source && !is(Source == enum) && * A $(LREF ConvException) if `source` is empty, if no number could be * parsed, or if an overflow occurred. */ -auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref scope Source source) +auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source) if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && isFloatingPoint!Target && !is(Target == enum)) { @@ -3115,18 +3115,17 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum static if (isNarrowString!Source) { import std.string : representation; - auto p = source.representation; + scope p = source.representation; } else { alias p = source; } - void advanceSource() @trusted + void advanceSource() { - // p is assigned from source.representation above so the cast is valid static if (isNarrowString!Source) - source = cast(Source) p; + source = source[$ - p.length .. $]; } static immutable real[14] negtab = @@ -5983,3 +5982,14 @@ if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) && } } } + +// Converts an unsigned integer to a compile-time string constant. +package enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length]; + +// Check that .stringof does what we expect, since it's not guaranteed by the +// language spec. +@safe /*@betterC*/ unittest +{ + assert(toCtString!0 == "0"); + assert(toCtString!123456 == "123456"); +} diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d index 949ad7d..e80f122 100644 --- a/libphobos/src/std/datetime/systime.d +++ b/libphobos/src/std/datetime/systime.d @@ -390,6 +390,33 @@ public: hnsecsToUnixEpoch; } } + else version (Hurd) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.hurd.time : CLOCK_REALTIME_COARSE; + import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; + else static assert(0, "Previous static if is wrong."); + timespec ts = void; + immutable error = clock_gettime(clockArg, &ts); + // Posix clock_gettime called with a valid address and valid clock_id is only + // permitted to fail if the number of seconds does not fit in time_t. If tv_sec + // is long or larger overflow won't happen before 292 billion years A.D. + static if (ts.tv_sec.max < long.max) + { + if (error) + throw new TimeException("Call to clock_gettime() failed"); + } + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + } else static assert(0, "Unsupported OS"); } else static assert(0, "Unsupported OS"); @@ -693,7 +720,7 @@ public: Returns: The `this` of this `SysTime`. +/ - ref SysTime opAssign()(auto ref const(SysTime) rhs) return scope @safe pure nothrow + ref SysTime opAssign()(auto ref const(SysTime) rhs) scope return @safe pure nothrow { _stdTime = rhs._stdTime; _timezone = rhs._timezone; diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d index afd98ad..6a28de5 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 import std.concurrency : initOnce; initOnce!stdSharedDefaultLogger({ auto buffer = cast(ubyte[]) _buffer; - return emplace!FileLogger(buffer, stderr, LogLevel.warning); + return emplace!FileLogger(buffer, stderr, LogLevel.info); }()); return stdSharedDefaultLogger; } /** This property sets and gets the default `Logger`. Unless set to another -logger by the user, the default logger's log level is LogLevel.warning. +logger by the user, the default logger's log level is LogLevel.info. Example: ------------- @@ -2008,7 +2008,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto oldunspecificLogger = sharedLog; - assert(oldunspecificLogger.logLevel == LogLevel.warning, + assert(oldunspecificLogger.logLevel == LogLevel.info, to!string(oldunspecificLogger.logLevel)); assert(l.logLevel == LogLevel.all); @@ -3064,7 +3064,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); - assert(dl.logLevel == LogLevel.warning); + assert(dl.logLevel == LogLevel.info); assert(globalLogLevel == LogLevel.all); auto tl = cast(StdForwardLogger) stdThreadLocalLog; diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d index a0bea77..5112e52 100644 --- a/libphobos/src/std/experimental/logger/filelogger.d +++ b/libphobos/src/std/experimental/logger/filelogger.d @@ -263,7 +263,7 @@ class FileLogger : Logger { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); - assert(dl.logLevel == LogLevel.warning); + assert(dl.logLevel == LogLevel.info); assert(globalLogLevel == LogLevel.all); auto tl = cast(StdForwardLogger) stdThreadLocalLog; diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d index 90bfb58..9acd23a 100644 --- a/libphobos/src/std/experimental/logger/multilogger.d +++ b/libphobos/src/std/experimental/logger/multilogger.d @@ -191,7 +191,7 @@ class MultiLogger : Logger { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); - assert(dl.logLevel == LogLevel.warning); + assert(dl.logLevel == LogLevel.info); assert(globalLogLevel == LogLevel.all); auto tl = cast(StdForwardLogger) stdThreadLocalLog; diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index b09b82a..6bc7d4d 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -214,7 +214,7 @@ class FileException : Exception string file = __FILE__, size_t line = __LINE__) @safe { - this(name, sysErrorString(errno), file, line, errno); + this(name, generateSysErrorMsg(errno), file, line, errno); } else version (Posix) this(scope const(char)[] name, uint errno = .errno, @@ -3471,7 +3471,7 @@ else version (Posix) string getcwd() @trusted while (true) { auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length); - enforce(len, sysErrorString(GetLastError())); + wenforce(len); if (len != buffer.length) return to!(string)(buffer[0 .. len]); buffer.length *= 2; @@ -3574,6 +3574,10 @@ else version (Posix) string getcwd() @trusted // Only Solaris 10 and later return readLink(format("/proc/%d/path/a.out", getpid())); } + else version (Hurd) + { + return readLink("/proc/self/exe"); + } else static assert(0, "thisExePath is not supported on this platform"); } diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d index 68b9e4d..f1d6964 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) enum hasToString = HasToStringResult.none; } else static if (is(typeof( - { - T val = void; + (T val) { const FormatSpec!Char f; static struct S {void put(scope Char s){}} S s; @@ -2015,8 +2014,7 @@ template hasToString(T, Char) enum hasToString = HasToStringResult.customPutWriterFormatSpec; } else static if (is(typeof( - { - T val = void; + (T val) { static struct S {void put(scope Char s){}} S s; val.toString(s); @@ -2026,36 +2024,36 @@ template hasToString(T, Char) { enum hasToString = HasToStringResult.customPutWriter; } - else static if (is(typeof({ T val = void; FormatSpec!Char f; val.toString((scope const(char)[] s){}, f); }))) + else static if (is(typeof((T val) { FormatSpec!Char f; val.toString((scope const(char)[] s){}, f); }))) { enum hasToString = HasToStringResult.constCharSinkFormatSpec; } - else static if (is(typeof({ T val = void; val.toString((scope const(char)[] s){}, "%s"); }))) + else static if (is(typeof((T val) { val.toString((scope const(char)[] s){}, "%s"); }))) { enum hasToString = HasToStringResult.constCharSinkFormatString; } - else static if (is(typeof({ T val = void; val.toString((scope const(char)[] s){}); }))) + else static if (is(typeof((T val) { val.toString((scope const(char)[] s){}); }))) { enum hasToString = HasToStringResult.constCharSink; } else static if (hasPreviewIn && - is(typeof({ T val = void; FormatSpec!Char f; val.toString((in char[] s){}, f); }))) + is(typeof((T val) { FormatSpec!Char f; val.toString((in char[] s){}, f); }))) { enum hasToString = HasToStringResult.inCharSinkFormatSpec; } else static if (hasPreviewIn && - is(typeof({ T val = void; val.toString((in char[] s){}, "%s"); }))) + is(typeof((T val) { val.toString((in char[] s){}, "%s"); }))) { enum hasToString = HasToStringResult.inCharSinkFormatString; } else static if (hasPreviewIn && - is(typeof({ T val = void; val.toString((in char[] s){}); }))) + is(typeof((T val) { val.toString((in char[] s){}); }))) { enum hasToString = HasToStringResult.inCharSink; } - else static if (is(typeof({ T val = void; return val.toString(); }()) S) && isSomeString!S) + else static if (is(ReturnType!((T val) { return val.toString(); }) S) && isSomeString!S) { enum hasToString = HasToStringResult.hasSomeToString; } @@ -2171,6 +2169,133 @@ template hasToString(T, Char) } } +// const toString methods +@safe unittest +{ + import std.range.primitives : isOutputRange; + + static struct A + { + void toString(Writer)(ref Writer w) const + if (isOutputRange!(Writer, string)) + {} + } + static struct B + { + void toString(scope void delegate(scope const(char)[]) sink, scope FormatSpec!char fmt) const {} + } + static struct C + { + void toString(scope void delegate(scope const(char)[]) sink, string fmt) const {} + } + static struct D + { + void toString(scope void delegate(scope const(char)[]) sink) const {} + } + static struct E + { + string toString() const {return "";} + } + static struct F + { + void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const + if (isOutputRange!(Writer, string)) + {} + } + static struct G + { + string toString() const {return "";} + void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} + } + static struct H + { + string toString() const {return "";} + void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const + if (isOutputRange!(Writer, string)) + {} + } + static struct I + { + void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, string)) {} + void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt) const + if (isOutputRange!(Writer, string)) + {} + } + static struct J + { + string toString() const {return "";} + void toString(Writer)(ref Writer w, scope ref FormatSpec!char fmt) const + if (isOutputRange!(Writer, string)) + {} + } + static struct K + { + void toString(Writer)(Writer w, scope const ref FormatSpec!char fmt) const + if (isOutputRange!(Writer, string)) + {} + } + static struct L + { + void toString(Writer)(ref Writer w, scope const FormatSpec!char fmt) const + if (isOutputRange!(Writer, string)) + {} + } + static struct M + { + void toString(scope void delegate(in char[]) sink, in FormatSpec!char fmt) const {} + } + static struct N + { + void toString(scope void delegate(in char[]) sink, string fmt) const {} + } + static struct O + { + void toString(scope void delegate(in char[]) sink) const {} + } + + with(HasToStringResult) + { + static assert(hasToString!(A, char) == customPutWriter); + static assert(hasToString!(B, char) == constCharSinkFormatSpec); + static assert(hasToString!(C, char) == constCharSinkFormatString); + static assert(hasToString!(D, char) == constCharSink); + static assert(hasToString!(E, char) == hasSomeToString); + static assert(hasToString!(F, char) == customPutWriterFormatSpec); + static assert(hasToString!(G, char) == customPutWriter); + static assert(hasToString!(H, char) == customPutWriterFormatSpec); + static assert(hasToString!(I, char) == customPutWriterFormatSpec); + static assert(hasToString!(J, char) == hasSomeToString); + static assert(hasToString!(K, char) == constCharSinkFormatSpec); + static assert(hasToString!(L, char) == none); + static if (hasPreviewIn) + { + static assert(hasToString!(M, char) == inCharSinkFormatSpec); + static assert(hasToString!(N, char) == inCharSinkFormatString); + static assert(hasToString!(O, char) == inCharSink); + } + + // https://issues.dlang.org/show_bug.cgi?id=22873 + static assert(hasToString!(inout(A), char) == customPutWriter); + static assert(hasToString!(inout(B), char) == constCharSinkFormatSpec); + static assert(hasToString!(inout(C), char) == constCharSinkFormatString); + static assert(hasToString!(inout(D), char) == constCharSink); + static assert(hasToString!(inout(E), char) == hasSomeToString); + static assert(hasToString!(inout(F), char) == customPutWriterFormatSpec); + static assert(hasToString!(inout(G), char) == customPutWriter); + static assert(hasToString!(inout(H), char) == customPutWriterFormatSpec); + static assert(hasToString!(inout(I), char) == customPutWriterFormatSpec); + static assert(hasToString!(inout(J), char) == hasSomeToString); + static assert(hasToString!(inout(K), char) == constCharSinkFormatSpec); + static assert(hasToString!(inout(L), char) == none); + static if (hasPreviewIn) + { + static assert(hasToString!(inout(M), char) == inCharSinkFormatSpec); + static assert(hasToString!(inout(N), char) == inCharSinkFormatString); + static assert(hasToString!(inout(O), char) == inCharSink); + } + } +} + // object formatting with toString private void formatObject(Writer, T, Char)(ref Writer w, ref T val, scope const ref FormatSpec!Char f) if (hasToString!(T, Char)) diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d index 6c9e9ae..76d68f6 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) if (isSomeString!(typeof(fmt))) { alias e = checkFormatException!(fmt, Args); - static assert(!e, e.msg); + static assert(!e, e); return .sformat(buf, fmt, args); } diff --git a/libphobos/src/std/format/read.d b/libphobos/src/std/format/read.d index b3d8d1f..144fa8c 100644 --- a/libphobos/src/std/format/read.d +++ b/libphobos/src/std/format/read.d @@ -305,7 +305,7 @@ if (isSomeString!(typeof(fmt))) import std.format : checkFormatException; alias e = checkFormatException!(fmt, Args); - static assert(!e, e.msg); + static assert(!e, e); return .formattedRead(r, fmt, args); } diff --git a/libphobos/src/std/format/write.d b/libphobos/src/std/format/write.d index e67d95c..cbab512 100644 --- a/libphobos/src/std/format/write.d +++ b/libphobos/src/std/format/write.d @@ -677,7 +677,7 @@ if (isSomeString!(typeof(fmt))) import std.format : checkFormatException; alias e = checkFormatException!(fmt, Args); - static assert(!e, e.msg); + static assert(!e, e); return .formattedWrite(w, fmt, args); } diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d index 90b0f91..70aaee3 100644 --- a/libphobos/src/std/functional.d +++ b/libphobos/src/std/functional.d @@ -65,6 +65,7 @@ module std.functional; import std.meta : AliasSeq, Reverse; import std.traits : isCallable, Parameters; +import std.conv : toCtString; import std.internal.attributes : betterC; @@ -1848,17 +1849,6 @@ if (isCallable!(F)) } } -// Converts an unsigned integer to a compile-time string constant. -private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length]; - -// Check that .stringof does what we expect, since it's not guaranteed by the -// language spec. -@safe unittest -{ - assert(toCtString!0 == "0"); - assert(toCtString!123456 == "123456"); -} - /** * Passes the fields of a struct as arguments to a function. * diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d index 89def0f..c6e746a 100644 --- a/libphobos/src/std/json.d +++ b/libphobos/src/std/json.d @@ -321,7 +321,7 @@ struct JSONValue (*a)[0] = "world"; // segmentation fault --- */ - @property ref inout(JSONValue[]) array() return scope inout pure @system + @property ref inout(JSONValue[]) array() scope return inout pure @system { enforce!JSONException(type == JSONType.array, "JSONValue is not an array"); diff --git a/libphobos/src/std/outbuffer.d b/libphobos/src/std/outbuffer.d index 9590238..c434481 100644 --- a/libphobos/src/std/outbuffer.d +++ b/libphobos/src/std/outbuffer.d @@ -331,7 +331,7 @@ class OutBuffer import std.format : checkFormatException; alias e = checkFormatException!(fmt, A); - static assert(!e, e.msg); + static assert(!e, e); return this.writef(fmt, args); } @@ -377,7 +377,7 @@ class OutBuffer import std.format : checkFormatException; alias e = checkFormatException!(fmt, A); - static assert(!e, e.msg); + static assert(!e, e); return this.writefln(fmt, args); } diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d index 971cfa0..2c97638 100644 --- a/libphobos/src/std/parallelism.d +++ b/libphobos/src/std/parallelism.d @@ -1039,6 +1039,11 @@ uint totalCPUsImpl() @nogc nothrow @trusted import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); } + else version (Hurd) + { + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); + } else { 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 2c68f36..28bfb04 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -299,10 +299,9 @@ static: } else version (Windows) { - import std.exception : enforce; - enforce( + import std.windows.syserror : wenforce; + wenforce( SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()), - sysErrorString(GetLastError()) ); return value; } @@ -1330,7 +1329,7 @@ private Pid spawnProcessWin(scope const(char)[] commandLine, { throw new StdioException( "Failed to make "~which~" stream inheritable by child process (" - ~sysErrorString(GetLastError()) ~ ')', + ~generateSysErrorMsg() ~ ')', 0); } } @@ -2775,7 +2774,7 @@ Pipe pipe() @trusted //TODO: @safe if (!CreatePipe(&readHandle, &writeHandle, null, 0)) { throw new StdioException( - "Error creating pipe (" ~ sysErrorString(GetLastError()) ~ ')', + "Error creating pipe (" ~ generateSysErrorMsg() ~ ')', 0); } @@ -3475,7 +3474,7 @@ class ProcessException : Exception string file = __FILE__, size_t line = __LINE__) { - auto lastMsg = sysErrorString(GetLastError()); + auto lastMsg = generateSysErrorMsg(); auto msg = customMsg.empty ? lastMsg : customMsg ~ " (" ~ lastMsg ~ ')'; return new ProcessException(msg, file, line); diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d index cd23232..915159f 100644 --- a/libphobos/src/std/socket.d +++ b/libphobos/src/std/socket.d @@ -169,7 +169,7 @@ string formatSocketError(int err) @trusted else version (Windows) { - return sysErrorString(err); + return generateSysErrorMsg(err); } else return "Socket error " ~ to!string(err); @@ -842,7 +842,7 @@ private string formatGaiError(int err) @trusted { version (Windows) { - return sysErrorString(err); + return generateSysErrorMsg(err); } else { diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index bc2d3fe..1bde73d 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. { import core.sys.windows.winbase : OVERLAPPED; import core.sys.windows.winnt : BOOL, ULARGE_INTEGER; + import std.windows.syserror : wenforce; private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, Flags flags) @@ -1474,15 +1475,6 @@ Throws: `Exception` if the file is not opened. return F(windowsHandle, flags, 0, liLength.LowPart, liLength.HighPart, &overlapped); } - - private static T wenforce(T)(T cond, lazy string str) - { - import core.sys.windows.winbase : GetLastError; - import std.windows.syserror : sysErrorString; - - if (cond) return cond; - throw new Exception(str ~ ": " ~ sysErrorString(GetLastError())); - } } version (Posix) { @@ -1783,7 +1775,7 @@ Throws: `Exception` if the file is not opened. import std.format : checkFormatException; alias e = checkFormatException!(fmt, A); - static assert(!e, e.msg); + static assert(!e, e); return this.writef(fmt, args); } @@ -1802,7 +1794,7 @@ Throws: `Exception` if the file is not opened. import std.format : checkFormatException; alias e = checkFormatException!(fmt, A); - static assert(!e, e.msg); + static assert(!e, e); return this.writefln(fmt, args); } @@ -2151,7 +2143,7 @@ $(CONSOLE import std.format : checkFormatException; alias e = checkFormatException!(format, Data); - static assert(!e, e.msg); + static assert(!e, e); return this.readf(format, data); } @@ -4388,7 +4380,7 @@ if (isSomeString!(typeof(fmt))) import std.format : checkFormatException; alias e = checkFormatException!(fmt, A); - static assert(!e, e.msg); + static assert(!e, e); return .writef(fmt, args); } @@ -4429,7 +4421,7 @@ if (isSomeString!(typeof(fmt))) import std.format : checkFormatException; alias e = checkFormatException!(fmt, A); - static assert(!e, e.msg); + static assert(!e, e); return .writefln(fmt, args); } @@ -4510,7 +4502,7 @@ if (isSomeString!(typeof(format))) import std.format : checkFormatException; alias e = checkFormatException!(format, A); - static assert(!e, e.msg); + static assert(!e, e); return .readf(format, args); } diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d index f4f4216..dac531f 100644 --- a/libphobos/src/std/sumtype.d +++ b/libphobos/src/std/sumtype.d @@ -237,21 +237,11 @@ import std.traits : ConstOf, ImmutableOf, InoutOf, TemplateArgsOf; import std.traits : CommonType, DeducedParameterType; import std.typecons : ReplaceTypeUnless; import std.typecons : Flag; +import std.conv : toCtString; /// Placeholder used to refer to the enclosing [SumType]. struct This {} -// Converts an unsigned integer to a compile-time string constant. -private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length]; - -// Check that .stringof does what we expect, since it's not guaranteed by the -// language spec. -@safe unittest -{ - assert(toCtString!0 == "0"); - assert(toCtString!123456 == "123456"); -} - // True if a variable of type T can appear on the lhs of an assignment private enum isAssignableTo(T) = isAssignable!T || (!isCopyable!T && isRvalueAssignable!T); diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index ea8f8bd..1ee7faa 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -3109,6 +3109,64 @@ struct Nullable(T) { return isNull ? fallback : _value.payload; } + + /// $(MREF_ALTTEXT Range interface, std, range, primitives) functions. + alias empty = isNull; + + /// ditto + alias popFront = nullify; + + /// ditto + alias popBack = nullify; + + /// ditto + @property ref inout(T) front() inout @safe pure nothrow + { + return get(); + } + + /// ditto + alias back = front; + + /// ditto + @property inout(typeof(this)) save() inout + { + return this; + } + + /// ditto + inout(typeof(this)) opIndex() inout + { + return this; + } + + /// ditto + inout(typeof(this)) opIndex(size_t[2] dim) inout + in (dim[0] <= length && dim[1] <= length && dim[1] >= dim[0]) + { + return (dim[0] == 0 && dim[1] == 1) ? this : this.init; + } + /// ditto + size_t[2] opSlice(size_t dim : 0)(size_t from, size_t to) const + { + return [from, to]; + } + + /// ditto + @property size_t length() const @safe pure nothrow + { + return !empty; + } + + /// ditto + alias opDollar(size_t dim : 0) = length; + + /// ditto + ref inout(T) opIndex(size_t index) inout @safe pure nothrow + in (index < length) + { + return get(); + } } /// ditto @@ -3162,6 +3220,23 @@ auto nullable(T)(T t) assert(a.isNull); assertThrown!Throwable(a.get); } +/// +@safe unittest +{ + import std.algorithm.iteration : each, joiner; + Nullable!int a = 42; + Nullable!int b; + // Add each value to an array + int[] arr; + a.each!((n) => arr ~= n); + assert(arr == [42]); + b.each!((n) => arr ~= n); + assert(arr == [42]); + // Take first value from an array of Nullables + Nullable!int[] c = new Nullable!int[](10); + c[7] = Nullable!int(42); + assert(c.joiner.front == 42); +} @safe unittest { auto k = Nullable!int(74); @@ -3638,6 +3713,42 @@ auto nullable(T)(T t) a = b = c = nullable(5); } +// https://issues.dlang.org/show_bug.cgi?id=18374 +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.range : only, takeNone; + import std.range.primitives : hasAssignableElements, hasLength, + hasLvalueElements, hasSlicing, hasSwappableElements, + isRandomAccessRange; + Nullable!int a = 42; + assert(!a.empty); + assert(a.front == 42); + assert(a.back == 42); + assert(a[0] == 42); + assert(a.equal(only(42))); + assert(a[0 .. $].equal(only(42))); + a[0] = 43; + assert(a.equal(only(43))); + --a[0]; + assert(a.equal(only(42))); + Nullable!int b; + assert(b.empty); + assert(b.equal(takeNone(b))); + Nullable!int c = a.save(); + assert(!c.empty); + c.popFront(); + assert(!a.empty); + assert(c.empty); + + assert(isRandomAccessRange!(Nullable!int)); + assert(hasLength!(Nullable!int)); + assert(hasSlicing!(Nullable!int)); + assert(hasAssignableElements!(Nullable!int)); + assert(hasSwappableElements!(Nullable!int)); + assert(hasLvalueElements!(Nullable!int)); +} + /** Just like `Nullable!T`, except that the null state is defined as a particular value. For example, $(D Nullable!(uint, uint.max)) is an @@ -8793,19 +8904,6 @@ private: enum isBaseEnumType(T) = is(E == T); alias Base = OriginalType!E; Base mValue; - static struct Negation - { - @safe @nogc pure nothrow: - private: - Base mValue; - - // Prevent non-copy construction outside the module. - @disable this(); - this(Base value) - { - mValue = value; - } - } public: this(E flag) @@ -8830,10 +8928,10 @@ public: return mValue; } - Negation opUnary(string op)() const + auto opUnary(string op)() const if (op == "~") { - return Negation(~mValue); + return BitFlags(cast(E) cast(Base) ~mValue); } auto ref opAssign(T...)(T flags) @@ -8877,12 +8975,6 @@ public: return this; } - auto ref opOpAssign(string op: "&")(Negation negatedFlags) - { - mValue &= negatedFlags.mValue; - return this; - } - auto opBinary(string op)(BitFlags flags) const if (op == "|" || op == "&") { @@ -8899,13 +8991,6 @@ public: return result; } - auto opBinary(string op: "&")(Negation negatedFlags) const - { - BitFlags result = this; - result.opOpAssign!op(negatedFlags); - return result; - } - auto opBinaryRight(string op)(E flag) const if (op == "|" || op == "&") { @@ -9104,6 +9189,34 @@ public: assert(flags.A && !flags.B && !flags.C); } +// Negation of BitFlags should work with any base type. +// Double-negation of BitFlags should work. +@safe @nogc pure nothrow unittest +{ + static foreach (alias Base; AliasSeq!( + byte, + ubyte, + short, + ushort, + int, + uint, + long, + ulong, + )) + {{ + enum Enum : Base + { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + } + + auto flags = BitFlags!Enum(Enum.A); + + assert(flags == ~~flags); + }} +} + private enum false_(T) = false; // ReplaceType diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d index eeeda72..98735ac 100644 --- a/libphobos/src/std/uni/package.d +++ b/libphobos/src/std/uni/package.d @@ -9834,25 +9834,29 @@ dchar toLower(dchar c) Returns: An array with the same element type as `s`. +/ -ElementEncodingType!S[] toLower(S)(S s) -if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) +ElementEncodingType!S[] toLower(S)(return scope S s) @trusted +if (isSomeString!S) { static import std.ascii; + return toCase!(LowerTriple, std.ascii.toLower)(s); +} - static if (isSomeString!S) - return () @trusted { return toCase!(LowerTriple, std.ascii.toLower)(s); } (); - else - return toCase!(LowerTriple, std.ascii.toLower)(s); +/// ditto +ElementEncodingType!S[] toLower(S)(S s) +if (!isSomeString!S && (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) +{ + static import std.ascii; + return toCase!(LowerTriple, std.ascii.toLower)(s); } // overloads for the most common cases to reduce compile time @safe pure /*TODO nothrow*/ { - string toLower(string s) + string toLower(return scope string s) { return toLower!string(s); } - wstring toLower(wstring s) + wstring toLower(return scope wstring s) { return toLower!wstring(s); } - dstring toLower(dstring s) + dstring toLower(return scope dstring s) { return toLower!dstring(s); } @safe unittest @@ -10038,25 +10042,29 @@ dchar toUpper(dchar c) Returns: An new array with the same element type as `s`. +/ -ElementEncodingType!S[] toUpper(S)(S s) -if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) +ElementEncodingType!S[] toUpper(S)(return scope S s) @trusted +if (isSomeString!S) { static import std.ascii; + return toCase!(UpperTriple, std.ascii.toUpper)(s); +} - static if (isSomeString!S) - return () @trusted { return toCase!(UpperTriple, std.ascii.toUpper)(s); } (); - else - return toCase!(UpperTriple, std.ascii.toUpper)(s); +/// ditto +ElementEncodingType!S[] toUpper(S)(S s) +if (!isSomeString!S && (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) +{ + static import std.ascii; + return toCase!(UpperTriple, std.ascii.toUpper)(s); } // overloads for the most common cases to reduce compile time @safe pure /*TODO nothrow*/ { - string toUpper(string s) + string toUpper(return scope string s) { return toUpper!string(s); } - wstring toUpper(wstring s) + wstring toUpper(return scope wstring s) { return toUpper!wstring(s); } - dstring toUpper(dstring s) + dstring toUpper(return scope dstring s) { return toUpper!dstring(s); } @safe unittest diff --git a/libphobos/src/std/windows/charset.d b/libphobos/src/std/windows/charset.d index 69626b5..ce33616 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) to!int(result.length), null, null); } - if (!readLen || readLen != result.length) - { - throw new Exception("Couldn't convert string: " ~ - sysErrorString(GetLastError())); - } - + wenforce(readLen && readLen == result.length, "Couldn't convert string"); return result.ptr; } } @@ -107,16 +102,10 @@ string fromMBSz(return scope immutable(char)* s, int codePage = 0) to!int(result.length)); } - if (!readLen || readLen != result.length) - { - throw new Exception("Couldn't convert string: " ~ - sysErrorString(GetLastError())); - } + wenforce(readLen && readLen == result.length, "Couldn't convert string"); return result[0 .. result.length-1].to!string; // omit trailing null } } return s[0 .. c-s]; // string is ASCII, no conversion necessary } - - diff --git a/libphobos/src/std/windows/syserror.d b/libphobos/src/std/windows/syserror.d index 94f8ee5..3d8c5e7 100644 --- a/libphobos/src/std/windows/syserror.d +++ b/libphobos/src/std/windows/syserror.d @@ -43,7 +43,7 @@ version (StdDdoc) { private alias DWORD = int; final @property DWORD code(); /// `GetLastError`'s return value. - this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted; + this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted; } /++ @@ -66,9 +66,9 @@ else: version (Windows): import core.sys.windows.winbase, core.sys.windows.winnt; -import std.array : appender; -import std.conv : to; -import std.format.write : formattedWrite; +import std.array : appender, Appender; +import std.conv : to, toTextRange, text; +import std.exception; import std.windows.charset; string sysErrorString( @@ -79,16 +79,25 @@ string sysErrorString( { auto buf = appender!string(); - if (!putSysError(errCode, buf, MAKELANGID(langId, subLangId))) - { - throw new Exception( - "failed getting error string for WinAPI error code: " ~ - sysErrorString(GetLastError())); - } + wenforce( + // Ignore unlikely UTF decoding errors, always report the actual error (`errCode`) + putSysError(errCode, buf, MAKELANGID(langId, subLangId)).ifThrown(false), + text("Could not fetch error string for WinAPI code ", errCode) + ); return buf.data; } +@safe unittest +{ + import std.algorithm.searching; + + assert(sysErrorString(ERROR_PATH_NOT_FOUND) !is null); + + const msg = collectExceptionMsg!WindowsException(sysErrorString(DWORD.max)); + assert(msg.startsWith(`Could not fetch error string for WinAPI code 4294967295: `)); +} + bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) { wchar *lpMsgBuf = null; @@ -114,7 +123,6 @@ bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0) return false; } - class WindowsException : Exception { import core.sys.windows.windef : DWORD; @@ -122,11 +130,11 @@ class WindowsException : Exception final @property DWORD code() { return _code; } /// `GetLastError`'s return value. private DWORD _code; - this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted + this(DWORD code, string str=null, string file = null, size_t line = 0) nothrow @trusted { _code = code; - auto buf = appender!string(); + auto buf = appender!(char[]); if (str != null) { @@ -135,18 +143,43 @@ class WindowsException : Exception buf.put(": "); } - if (code) + if (code && writeErrorMessage(code, buf)) { - auto success = putSysError(code, buf); - formattedWrite(buf, success ? " (error %d)" : "Error %d", code); + buf.put(" (error "); + toTextRange(code, buf); + buf.put(')'); } - super(buf.data, file, line); + super(cast(immutable) buf.data, file, line); } } +/// Writes the error string associated to `code` into `buf`. +/// Writes `Error <code>` when the error message lookup fails +private bool writeErrorMessage(DWORD code, ref Appender!(char[]) buf) nothrow +{ + bool success; + try + { + // Reset the buffer to undo partial changes + const len = buf[].length; + scope (failure) buf.shrinkTo(len); -T wenforce(T, S)(T value, lazy S msg = null, + success = putSysError(code, buf); + } + catch (Exception) {} + + // Write the error code instead if we couldn't find the string + if (!success) + { + buf.put("Error "); + toTextRange(code, buf); + } + + return success; +} + +T wenforce(T, S = string)(T value, lazy S msg = null, string file = __FILE__, size_t line = __LINE__) if (isSomeString!S) { @@ -177,11 +210,9 @@ T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file throw new WindowsException(GetLastError(), names, file, line); } -version (Windows) @system unittest { import std.algorithm.searching : startsWith, endsWith; - import std.exception; import std.string; auto e = collectException!WindowsException( @@ -199,3 +230,29 @@ version (Windows) e = new WindowsException(0, "Test"); assert(e.msg == "Test"); } + +@safe nothrow unittest +{ + import std.algorithm.searching : endsWith; + + auto e = new WindowsException(ERROR_FILE_NOT_FOUND); + assert(e.msg.endsWith("(error 2)")); + + e = new WindowsException(DWORD.max); + assert(e.msg == "Error 4294967295"); +} + +/// Tries to translate an error code from the Windows API to the corresponding +/// error message. Returns `Error <code>` on failure +package (std) string generateSysErrorMsg(DWORD errCode = GetLastError()) nothrow @trusted +{ + auto buf = appender!(char[]); + cast(void) writeErrorMessage(errCode, buf); + return cast(immutable) buf[]; +} + +nothrow @safe unittest +{ + assert(generateSysErrorMsg(ERROR_PATH_NOT_FOUND) !is null); + assert(generateSysErrorMsg(DWORD.max) == "Error 4294967295"); +} 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 0000000..64092f0 --- /dev/null +++ b/libphobos/testsuite/libphobos.exceptions/message_with_null.d @@ -0,0 +1,8 @@ +// { dg-shouldfail " world!" } +// { dg-output "object.Exception@.*: hello.*world!" } +module message_with_null; + +void main() +{ + throw new Exception("hello\0 world!"); +} |