diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2022-07-26 17:42:23 +0200 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2022-08-03 13:01:53 +0200 |
commit | b6df113247b9f3f7c3db0e65c481dad5bcfddfb4 (patch) | |
tree | 31466a07292ad0cc289de7c23e39ba31b9e8b7c3 /libphobos/src | |
parent | 64ce76d940501cb04d14a0d36752b4f93473531c (diff) | |
download | gcc-b6df113247b9f3f7c3db0e65c481dad5bcfddfb4.tar.gz |
d: Merge upstream dmd d7772a2369, phobos 5748ca43f.
In upstream dmd, the compiler front-end and run-time have been merged
together into one repository. Both dmd and libdruntime now track that.
D front-end changes:
- Deprecated `scope(failure)' blocks that contain `return' statements.
- Deprecated using integers for `version' or `debug' conditions.
- Deprecated returning a discarded void value from a function.
- `new' can now allocate an associative array.
D runtime changes:
- Added avx512f detection to core.cpuid module.
Phobos changes:
- Changed std.experimental.logger.core.sharedLog to return
shared(Logger).
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd d7772a2369.
* dmd/VERSION: Bump version to v2.100.1.
* d-codegen.cc (get_frameinfo): Check whether decision to generate
closure changed since semantic finished.
* d-lang.cc (d_handle_option): Remove handling of -fdebug=level and
-fversion=level.
* decl.cc (DeclVisitor::visit (VarDeclaration *)): Generate evaluation
of noreturn variable initializers before throw.
* expr.cc (ExprVisitor::visit (AssignExp *)): Don't generate
assignment for noreturn types, only evaluate for side effects.
* lang.opt (fdebug=): Undocument -fdebug=level.
(fversion=): Undocument -fversion=level.
libphobos/ChangeLog:
* configure: Regenerate.
* configure.ac (libtool_VERSION): Update to 4:0:0.
* libdruntime/MERGE: Merge upstream druntime d7772a2369.
* libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add
core/internal/array/duplication.d.
* libdruntime/Makefile.in: Regenerate.
* src/MERGE: Merge upstream phobos 5748ca43f.
* testsuite/libphobos.gc/nocollect.d:
Diffstat (limited to 'libphobos/src')
-rw-r--r-- | libphobos/src/MERGE | 2 | ||||
-rw-r--r-- | libphobos/src/etc/c/curl.d | 18 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/comparison.d | 2 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/iteration.d | 15 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/searching.d | 12 | ||||
-rw-r--r-- | libphobos/src/std/conv.d | 19 | ||||
-rw-r--r-- | libphobos/src/std/experimental/checkedint.d | 2 | ||||
-rw-r--r-- | libphobos/src/std/experimental/logger/core.d | 176 | ||||
-rw-r--r-- | libphobos/src/std/experimental/logger/filelogger.d | 2 | ||||
-rw-r--r-- | libphobos/src/std/experimental/logger/multilogger.d | 2 | ||||
-rw-r--r-- | libphobos/src/std/file.d | 2 | ||||
-rw-r--r-- | libphobos/src/std/format/internal/write.d | 6 | ||||
-rw-r--r-- | libphobos/src/std/format/package.d | 24 | ||||
-rw-r--r-- | libphobos/src/std/math/package.d | 1 | ||||
-rw-r--r-- | libphobos/src/std/math/rounding.d | 28 | ||||
-rw-r--r-- | libphobos/src/std/random.d | 67 | ||||
-rw-r--r-- | libphobos/src/std/stdio.d | 17 | ||||
-rw-r--r-- | libphobos/src/std/sumtype.d | 148 | ||||
-rw-r--r-- | libphobos/src/std/typecons.d | 20 | ||||
-rw-r--r-- | libphobos/src/std/uni/package.d | 8 |
20 files changed, 387 insertions, 184 deletions
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 744e5ad5e78..1f0cfbf3e29 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -1516ecad932d88a1618163384e6f69009d125391 +5748ca43fd5c3e31ce7a8511f542b67e5d5a3dc6 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/etc/c/curl.d b/libphobos/src/etc/c/curl.d index 0c5b727944c..e6a10435d2b 100644 --- a/libphobos/src/etc/c/curl.d +++ b/libphobos/src/etc/c/curl.d @@ -1372,9 +1372,9 @@ alias curl_TimeCond = int; /** curl_strequal() and curl_strnequal() are subject for removal in a future libcurl, see lib/README.curlx for details */ extern (C) { -int curl_strequal(in const(char) *s1, in const(char) *s2); +int curl_strequal(scope const(char) *s1, scope const(char) *s2); /// ditto -int curl_strnequal(in const(char) *s1, in const(char) *s2, size_t n); +int curl_strnequal(scope const(char) *s1, scope const(char) *s2, size_t n); } enum CurlForm { nothing, /********** the first one is unused ************/ @@ -1464,7 +1464,7 @@ CURLFORMcode curl_formadd(curl_httppost **httppost, curl_httppost **last_post,. * Should return the buffer length passed to it as the argument "len" on * success. */ -alias curl_formget_callback = size_t function(void *arg, in const(char) *buf, size_t len); +alias curl_formget_callback = size_t function(void *arg, const(char) *buf, size_t len); /** * Name: curl_formget() @@ -1494,7 +1494,7 @@ void curl_formfree(curl_httppost *form); * Returns a malloc()'ed string that MUST be curl_free()ed after usage is * complete. DEPRECATED - see lib/README.curlx */ -char * curl_getenv(in const(char) *variable); +char * curl_getenv(scope const(char) *variable); /** * Name: curl_version() @@ -1514,10 +1514,10 @@ char * curl_version(); * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -char * curl_easy_escape(CURL *handle, in const(char) *string, int length); +char * curl_easy_escape(CURL *handle, scope const(char) *string, int length); /** the previous version: */ -char * curl_escape(in const(char) *string, int length); +char * curl_escape(scope const(char) *string, int length); /** @@ -1531,10 +1531,10 @@ char * curl_escape(in const(char) *string, int length); * Conversion Note: On non-ASCII platforms the ASCII %XX codes are * converted into the host encoding. */ -char * curl_easy_unescape(CURL *handle, in const(char) *string, int length, int *outlength); +char * curl_easy_unescape(CURL *handle, scope const(char) *string, int length, int *outlength); /** the previous version */ -char * curl_unescape(in const(char) *string, int length); +char * curl_unescape(scope const(char) *string, int length); /** * Name: curl_free() @@ -1608,7 +1608,7 @@ struct curl_slist * Appends a string to a linked list. If no list exists, it will be created * first. Returns the new list, after appending. */ -curl_slist * curl_slist_append(curl_slist *, in const(char) *); +curl_slist * curl_slist_append(curl_slist *, const(char) *); /** * Name: curl_slist_free_all() diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d index 2fcc2bacd5c..b810fbb9258 100644 --- a/libphobos/src/std/algorithm/comparison.d +++ b/libphobos/src/std/algorithm/comparison.d @@ -1027,7 +1027,7 @@ template equal(alias pred = "a == b") } } - private bool equalLoop(Rs...)(Rs rs) + private bool equalLoop(Rs...)(ref Rs rs) { for (; !rs[0].empty; rs[0].popFront) static foreach (r; rs[1 .. $]) diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index af665c41197..300a8978fe5 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -1263,19 +1263,22 @@ public: // filter /** -Implements the higher order filter function. The predicate is passed to -$(REF unaryFun, std,functional), and can either accept a string, or any callable -that can be executed via `pred(element)`. +`filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for +which `predicate(x)` returns `true`. + +The predicate is passed to $(REF unaryFun, std,functional), and can be either a string, or +any callable that can be executed via `pred(element)`. Params: predicate = Function to apply to each element of range Returns: - `filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for - which `predicate(x)` returns `true`. + An input range that contains the filtered elements. If `range` is at least a forward range, the return value of `filter` + will also be a forward range. See_Also: - $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)) + $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)), + $(REF filterBidirectional, std,algorithm,iteration) */ template filter(alias predicate) if (is(typeof(unaryFun!predicate))) diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index 55a14385e8a..daa4b99045e 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -2512,6 +2512,8 @@ RandomAccessRange find(RandomAccessRange, alias pred, InputRange)( Convenience function. Like find, but only returns whether or not the search was successful. +For more information about `pred` see $(LREF find). + See_Also: $(REF among, std,algorithm,comparison) for checking a value against multiple possibilities. +/ @@ -2622,6 +2624,8 @@ Advances `r` until it finds the first two adjacent elements `a`, `b` that satisfy `pred(a, b)`. Performs $(BIGOH r.length) evaluations of `pred`. +For more information about `pred` see $(LREF find). + Params: pred = The predicate to satisfy. r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to @@ -2698,6 +2702,8 @@ Advances `seq` by calling `seq.popFront` until either `find!(pred)(choices, seq.front)` is `true`, or `seq` becomes empty. Performs $(BIGOH seq.length * choices.length) evaluations of `pred`. +For more information about `pred` see $(LREF find). + Params: pred = The predicate to use for determining a match. seq = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to @@ -2758,6 +2764,8 @@ if (isInputRange!InputRange && isForwardRange!ForwardRange) * Similarly, the haystack is positioned so as `pred` evaluates to `false` for * `haystack.front`. * + * For more information about `pred` see $(LREF find). + * Params: * haystack = The * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search @@ -2882,6 +2890,8 @@ $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the type of `result[0]` and `result[1]` is the same as $(REF takeExactly, std,range). +For more information about `pred` see $(LREF find). + Params: pred = Predicate to use for comparing needle against haystack. haystack = The range to search. @@ -4595,6 +4605,8 @@ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one of) the given needle(s) or, if no needles are given, if its front element fulfils predicate `pred`. +For more information about `pred` see $(LREF find). + Params: pred = Predicate to use in comparing the elements of the haystack and the diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 8f6c3bf568e..9164e079865 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -3419,17 +3419,20 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum } } + Target result = cast(Target) (sign ? -ldval : ldval); + // if overflow occurred - enforce(ldval != real.infinity, new ConvException("Range error")); + import std.math : isFinite; + enforce(isFinite(result), new ConvException("Range error")); advanceSource(); static if (doCount) { - return tuple!("data", "count")(cast (Target) (sign ? -ldval : ldval), count); + return tuple!("data", "count")(result, count); } else { - return cast (Target) (sign ? -ldval : ldval); + return result; } } @@ -3785,6 +3788,16 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum assertThrown!ConvException(parse!double(s)); } +@safe unittest // https://issues.dlang.org/show_bug.cgi?id=22637 +{ + import std.exception : assertThrown, assertNotThrown; + auto src = "9991232549867999698999493543521458974414359998784641646846435132132543645435456345634541999999999999999" + ~ "9999999943321231321311999231345312413646846354354354399999934153465464654646464654134135354199999999996515734999" + ~ "9999999320135273486741354354731567431324134999999999999999999999999999999999999999999999135411.9"; + assertThrown!ConvException(parse!double(src)); + static if (real.max_10_exp > 310) assertNotThrown!ConvException(parse!real(src)); +} + /** Parsing one character off a range returns the first element and calls `popFront`. diff --git a/libphobos/src/std/experimental/checkedint.d b/libphobos/src/std/experimental/checkedint.d index 9237341d418..2be5a2ea150 100644 --- a/libphobos/src/std/experimental/checkedint.d +++ b/libphobos/src/std/experimental/checkedint.d @@ -1,6 +1,6 @@ /** - * This module is now deprecated, use $(MREF std, experimental) + * This module is now deprecated, use $(MREF std, checkedint) * instead. * * Copyright: Copyright The D Language Foundation 2005 - 2015. diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d index d899db70793..f3c69324ba1 100644 --- a/libphobos/src/std/experimental/logger/core.d +++ b/libphobos/src/std/experimental/logger/core.d @@ -4,6 +4,7 @@ Source: $(PHOBOSSRC std/experimental/logger/core.d) */ module std.experimental.logger.core; +import core.atomic : atomicLoad, atomicOp, atomicStore, MemoryOrder; import core.sync.mutex : Mutex; import std.datetime.date : DateTime; import std.datetime.systime : Clock, SysTime; @@ -555,14 +556,14 @@ abstract class Logger Params: lv = `LogLevel` to use for this `Logger` instance. */ - this(LogLevel lv) @safe + this(this This)(LogLevel lv) { this.logLevel_ = lv; this.fatalHandler_ = delegate() { throw new Error("A fatal log message was logged"); }; - this.mutex = new Mutex(); + this.mutex = new typeof(mutex)(); } /** A custom logger must implement this method in order to work in a @@ -661,7 +662,7 @@ abstract class Logger /// Ditto @property final void logLevel(const LogLevel lv) @safe @nogc { - synchronized (mutex) this.logLevel_ = lv; + atomicStore(this.logLevel_, lv); } /** This `delegate` is called in case a log message with @@ -1403,28 +1404,28 @@ abstract class Logger // Thread Global -private __gshared Logger stdSharedDefaultLogger; +private shared Logger stdSharedDefaultLogger; private shared Logger stdSharedLogger; private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; /* This method returns the global default Logger. * Marked @trusted because of excessive reliance on __gshared data */ -private @property Logger defaultSharedLoggerImpl() @trusted +private @property shared(Logger) defaultSharedLoggerImpl() @trusted { import core.lifetime : emplace; import std.stdio : stderr; __gshared align(__traits(classInstanceAlignment, FileLogger)) - void[__traits(classInstanceSize, FileLogger)] _buffer; + void[__traits(classInstanceSize, FileLogger)] _buffer = void; import std.concurrency : initOnce; initOnce!stdSharedDefaultLogger({ auto buffer = cast(ubyte[]) _buffer; - return emplace!FileLogger(buffer, stderr, LogLevel.info); + return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info); }()); - return stdSharedDefaultLogger; + return atomicLoad(stdSharedDefaultLogger); } /** This property sets and gets the default `Logger`. Unless set to another @@ -1452,19 +1453,12 @@ if (sharedLog !is myLogger) sharedLog = new myLogger; ------------- */ -@property Logger sharedLog() @safe +@property shared(Logger) sharedLog() @safe { - static auto trustedLoad(ref shared Logger logger) @trusted - { - import core.atomic : atomicLoad, MemoryOrder; - return cast() atomicLoad!(MemoryOrder.acq)(logger); - //FIXME: Casting shared away here. Not good. See issue 16232. - } - // If we have set up our own logger use that - if (auto logger = trustedLoad(stdSharedLogger)) + if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger)) { - return logger; + return atomicLoad(logger); } else { @@ -1474,10 +1468,9 @@ if (sharedLog !is myLogger) } /// Ditto -@property void sharedLog(Logger logger) @trusted +@property void sharedLog(shared(Logger) logger) @safe { - import core.atomic : atomicStore, MemoryOrder; - atomicStore!(MemoryOrder.rel)(stdSharedLogger, cast(shared) logger); + atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger)); } /** This methods get and set the global `LogLevel`. @@ -1523,9 +1516,12 @@ class StdForwardLogger : Logger this.fatalHandler = delegate() {}; } - override protected void writeLogMsg(ref LogEntry payload) + override protected void writeLogMsg(ref LogEntry payload) @trusted { - sharedLog.forwardMsg(payload); + synchronized (sharedLog.mutex) + { + (cast() sharedLog).forwardMsg(payload); + } } } @@ -1535,6 +1531,40 @@ class StdForwardLogger : Logger auto nl1 = new StdForwardLogger(LogLevel.all); } +@safe unittest +{ + import core.thread : Thread, msecs; + + static class RaceLogger : Logger + { + int value; + this() @safe shared + { + super(LogLevel.init); + } + override void writeLogMsg(ref LogEntry payload) @safe + { + import core.thread : Thread, msecs; + if (payload.msg == "foo") + { + value = 42; + () @trusted { Thread.sleep(100.msecs); }(); + assert(value == 42, "Another thread changed the value"); + } + else + { + () @trusted { Thread.sleep(50.msecs); } (); + value = 13; + } + } + } + + sharedLog = new shared RaceLogger; + scope(exit) { sharedLog = null; } + () @trusted { new Thread(() { log("foo"); }).start(); }(); + log("bar"); +} + /** This `LogLevel` is unqiue to every thread. The thread local `Logger` will use this `LogLevel` to filter log calls @@ -1561,7 +1591,7 @@ private @property Logger stdThreadLocalLogImpl() @trusted } /** This function returns a thread unique `Logger`, that by default -propergates all data logged to it to the `sharedLog`. +propagates all data logged to it to the `sharedLog`. These properties can be used to set and get this `Logger`. Every modification to this `Logger` will only be visible in the thread the @@ -1671,10 +1701,12 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto oldunspecificLogger = sharedLog; scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); } - sharedLog = tl1; + () @trusted { + sharedLog = cast(shared) tl1; + }(); log(); assert(tl1.line == __LINE__ - 1); @@ -1793,22 +1825,34 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe assert(l.line == lineNumber); assert(l.logLevel == LogLevel.all); - auto oldunspecificLogger = sharedLog; + Logger oldunspecificLogger; + () @trusted { + oldunspecificLogger = cast() sharedLog; + }(); assert(oldunspecificLogger.logLevel == LogLevel.info, to!string(oldunspecificLogger.logLevel)); assert(l.logLevel == LogLevel.all); - sharedLog = l; + + () @trusted { + sharedLog = cast(shared) l; + }(); + assert(globalLogLevel == LogLevel.all, to!string(globalLogLevel)); scope(exit) { - sharedLog = oldunspecificLogger; + () @trusted { + sharedLog = atomicLoad(cast(shared) oldunspecificLogger); + }(); } - assert(sharedLog.logLevel == LogLevel.all); + () @trusted { + assert((cast() sharedLog).logLevel == LogLevel.all); + }(); + assert(stdThreadLocalLog.logLevel == LogLevel.all); assert(globalLogLevel == LogLevel.all); @@ -1880,13 +1924,14 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; FileLogger l = new FileLogger(filename); auto oldunspecificLogger = sharedLog; - sharedLog = l; + + sharedLog = cast(shared) l; scope(exit) { remove(filename); assert(!exists(filename)); - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -1923,7 +1968,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe scope(exit) { remove(filename); - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -1931,8 +1976,11 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe string written = "this should be written to file"; auto l = new FileLogger(filename); - sharedLog = l; - sharedLog.logLevel = LogLevel.critical; + sharedLog = cast(shared) l; + + () @trusted { + (cast() sharedLog).logLevel = LogLevel.critical; + }(); log(LogLevel.error, false, notWritten); log(LogLevel.critical, true, written); @@ -1974,11 +2022,14 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto mem = new TestLogger; mem.fatalHandler = delegate() {}; - sharedLog = mem; + + () @trusted { + sharedLog = cast(shared) mem; + }(); scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2221,11 +2272,14 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto mem = new TestLogger; mem.fatalHandler = delegate() {}; - sharedLog = mem; + + () @trusted { + sharedLog = cast(shared) mem; + }(); scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2477,10 +2531,13 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe stdThreadLocalLog.logLevel = LogLevel.all; - sharedLog = mem; + () @trusted { + sharedLog = cast(shared) mem; + }(); + scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2707,12 +2764,15 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } auto tl = new TestLogger(LogLevel.info); - sharedLog = tl; + + () @trusted { + sharedLog = cast(shared) tl; + }(); trace("trace"); assert(tl.msg.indexOf("trace") == -1); @@ -2730,7 +2790,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2738,7 +2798,10 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto tl = new TestLogger(LogLevel.info); logger.insertLogger("required", tl); - sharedLog = logger; + + () @trusted { + sharedLog = cast(shared) logger; + }(); trace("trace"); assert(tl.msg.indexOf("trace") == -1); @@ -2774,14 +2837,12 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe // Workaround for atomics not allowed in @safe code private auto trustedLoad(T)(ref shared T value) @trusted { - import core.atomic : atomicLoad, MemoryOrder; return atomicLoad!(MemoryOrder.acq)(value); } // ditto private void trustedStore(T)(ref shared T dst, ref T src) @trusted { - import core.atomic : atomicStore, MemoryOrder; atomicStore!(MemoryOrder.rel)(dst, src); } @@ -2789,7 +2850,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted // to shared logger @system unittest { - import core.atomic, core.thread, std.concurrency; + import core.thread, std.concurrency; static shared logged_count = 0; @@ -2826,10 +2887,13 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted auto oldSharedLog = sharedLog; scope(exit) { - sharedLog = oldSharedLog; + sharedLog = atomicLoad(oldSharedLog); } - sharedLog = new IgnoredLog; + () @trusted { + sharedLog = cast(shared) new IgnoredLog; + }(); + Thread[] spawned; foreach (i; 0 .. 4) @@ -2849,7 +2913,9 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted @safe unittest { - auto dl = cast(FileLogger) sharedLog; + auto dl = () @trusted { + return cast(FileLogger) cast() sharedLog; + }(); assert(dl !is null); assert(dl.logLevel == LogLevel.info); assert(globalLogLevel == LogLevel.all); @@ -2946,7 +3012,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted auto oldShared = sharedLog; scope(exit) { - sharedLog = oldShared; + sharedLog = atomicLoad(oldShared); if (exists(fn)) { remove(fn); @@ -2956,7 +3022,11 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted auto ts = [ "Test log 1", "Test log 2", "Test log 3"]; auto fl = new FileLogger(fn); - sharedLog = fl; + + () @trusted { + sharedLog = cast(shared) fl; + }(); + assert(exists(fn)); foreach (t; ts) diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d index 5112e520bc0..457012e21b5 100644 --- a/libphobos/src/std/experimental/logger/filelogger.d +++ b/libphobos/src/std/experimental/logger/filelogger.d @@ -259,7 +259,7 @@ class FileLogger : Logger file.close(); } -@safe unittest +@system unittest { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d index 9acd23a59cd..35936901964 100644 --- a/libphobos/src/std/experimental/logger/multilogger.d +++ b/libphobos/src/std/experimental/logger/multilogger.d @@ -187,7 +187,7 @@ class MultiLogger : Logger assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg); } -@safe unittest +@system unittest { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index b8b4a8ce6c6..d6cac41ef04 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -1510,7 +1510,7 @@ private ushort bitmapcount, reserved; attrgroup_t commonattr, volattr, dirattr, fileattr, forkattr; } - extern(C) int setattrlist(in char* path, scope ref attrlist attrs, + extern(C) int setattrlist(scope const(char)* path, scope ref attrlist attrs, scope void* attrbuf, size_t attrBufSize, c_ulong options) nothrow @nogc @system; } diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d index f1d69643495..2fd6ff72990 100644 --- a/libphobos/src/std/format/internal/write.d +++ b/libphobos/src/std/format/internal/write.d @@ -1337,7 +1337,7 @@ if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToSt /* Static-size arrays are formatted as dynamic arrays. */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref const(T) obj, +void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref T obj, scope const ref FormatSpec!Char f) if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { @@ -1782,13 +1782,13 @@ void formatChar(Writer)(ref Writer w, in dchar c, in char quote) Associative arrays are formatted by using `':'` and $(D ", ") as separators, and enclosed by `'['` and `']'`. */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) +void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f) if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { import std.format : enforceFmt, formatValue; import std.range.primitives : put; - AssocArrayTypeOf!(const(T)) val = obj; + AssocArrayTypeOf!T val = obj; const spec = f.spec; enforceFmt(spec == 's' || spec == '(', diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d index 76d68f6cff6..3f6f33adf6e 100644 --- a/libphobos/src/std/format/package.d +++ b/libphobos/src/std/format/package.d @@ -1356,6 +1356,30 @@ if (isSomeChar!Char) assert(result == " 1"); } +// https://issues.dlang.org/show_bug.cgi?id=23245 +@safe unittest +{ + static struct S + { + string toString() { return "S"; } + } + + S[1] s; + assert(format("%s", s) == "[S]"); +} + +// https://issues.dlang.org/show_bug.cgi?id=23246 +@safe unittest +{ + static struct S + { + string toString() { return "S"; } + } + + S[int] s = [0 : S()]; + assert(format("%s", s) == "[0:S]"); +} + /// ditto typeof(fmt) format(alias fmt, Args...)(Args args) if (isSomeString!(typeof(fmt))) diff --git a/libphobos/src/std/math/package.d b/libphobos/src/std/math/package.d index 7443b0dea2e..19982ec216a 100644 --- a/libphobos/src/std/math/package.d +++ b/libphobos/src/std/math/package.d @@ -383,6 +383,7 @@ template floatTraits(T) enum ushort EXPBIAS = 0x3FE0; enum uint EXPMASK_INT = 0x7FF0_0000; enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only + enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF; enum realFormat = RealFormat.ieeeDouble; version (LittleEndian) { diff --git a/libphobos/src/std/math/rounding.d b/libphobos/src/std/math/rounding.d index 5c8d708c489..7dbe89b2dee 100644 --- a/libphobos/src/std/math/rounding.d +++ b/libphobos/src/std/math/rounding.d @@ -908,7 +908,9 @@ T floorImpl(T)(const T x) @trusted pure nothrow @nogc // Other kinds of extractors for real formats. static if (F.realFormat == RealFormat.ieeeSingle) - int vi; + uint vi; + else static if (F.realFormat == RealFormat.ieeeDouble) + ulong vi; } floatBits y = void; y.rv = x; @@ -919,15 +921,14 @@ T floorImpl(T)(const T x) @trusted pure nothrow @nogc static if (F.realFormat == RealFormat.ieeeSingle) { int exp = ((y.vi >> (T.mant_dig - 1)) & 0xff) - 0x7f; + enum mantissa_mask = F.MANTISSAMASK_INT; + enum sign_shift = 31; } else static if (F.realFormat == RealFormat.ieeeDouble) { - int exp = ((y.vu[F.EXPPOS_SHORT] >> 4) & 0x7ff) - 0x3ff; - - version (LittleEndian) - int pos = 0; - else - int pos = 3; + long exp = ((y.vi >> (T.mant_dig - 1)) & 0x7ff) - 0x3ff; + enum mantissa_mask = F.MANTISSAMASK_LONG; + enum sign_shift = 63; } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) @@ -959,18 +960,21 @@ T floorImpl(T)(const T x) @trusted pure nothrow @nogc return 0.0; } - static if (F.realFormat == RealFormat.ieeeSingle) + static if (F.realFormat == RealFormat.ieeeSingle || + F.realFormat == RealFormat.ieeeDouble) { if (exp < (T.mant_dig - 1)) { // Clear all bits representing the fraction part. - const uint fraction_mask = F.MANTISSAMASK_INT >> exp; + // Note: the fraction mask represents the floating point number 0.999999... + // i.e: `2.0 ^^ (exp - T.mant_dig + 1) * (fraction_mask + 1) == 1.0` + const fraction_mask = mantissa_mask >> exp; if ((y.vi & fraction_mask) != 0) { - // If 'x' is negative, then first substract 1.0 from the value. - if (y.vi < 0) - y.vi += 0x00800000 >> exp; + // If 'x' is negative, then first substract (1.0 - T.epsilon) from the value. + if (y.vi >> sign_shift) + y.vi += fraction_mask; y.vi &= ~fraction_mask; } } diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index 106e51ceedb..b2206ce56ba 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -2762,7 +2762,7 @@ Returns: return a `ref` to the $(D range element), otherwise it will return a copy. */ -auto ref choice(Range, RandomGen = Random)(auto ref Range range, ref RandomGen urng) +auto ref choice(Range, RandomGen = Random)(Range range, ref RandomGen urng) if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) { assert(range.length > 0, @@ -2772,7 +2772,22 @@ if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) } /// ditto -auto ref choice(Range)(auto ref Range range) +auto ref choice(Range)(Range range) +{ + return choice(range, rndGen); +} + +/// ditto +auto ref choice(Range, RandomGen = Random)(ref Range range, ref RandomGen urng) +if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) +{ + assert(range.length > 0, + __PRETTY_FUNCTION__ ~ ": invalid Range supplied. Range cannot be empty"); + return range[uniform(size_t(0), $, urng)]; +} + +/// ditto +auto ref choice(Range)(ref Range range) { return choice(range, rndGen); } @@ -2827,6 +2842,39 @@ auto ref choice(Range)(auto ref Range range) "Choice did not return a valid element from the given Range"); } +@safe unittest // issue 18631 +{ + auto rng = MinstdRand0(42); + const a = [0,1,2]; + const(int[]) b = [0, 1, 2]; + auto x = choice(a); + auto y = choice(b); + auto z = choice(cast(const)[1, 2, 3]); + auto x1 = choice(a, rng); + auto y1 = choice(b, rng); + auto z1 = choice(cast(const)[1, 2, 3], rng); +} + +@safe unittest // Ref range (issue 18631 PR) +{ + struct TestRange + { + int x; + ref int front() return {return x;} + ref int back() return {return x;} + void popFront() {} + void popBack() {} + bool empty = false; + TestRange save() {return this;} + size_t length = 10; + alias opDollar = length; + ref int opIndex(size_t i) return {return x;} + } + + TestRange r = TestRange(10); + int* s = &choice(r); +} + /** Shuffles elements of `r` using `gen` as a shuffler. `r` must be a random-access range with length. If no RNG is specified, `rndGen` @@ -3008,8 +3056,16 @@ if (isRandomAccessRange!Range) } /** -Rolls a dice with relative probabilities stored in $(D -proportions). Returns the index in `proportions` that was chosen. +Get a random index into a list of weights corresponding to each index + +Similar to rolling a die with relative probabilities stored in `proportions`. +Returns the index in `proportions` that was chosen. + +Note: + Usually, dice are 'fair', meaning that each side has equal probability + to come up, in which case `1 + uniform(0, 6)` can simply be used. + In future Phobos versions, this function might get renamed to something like + `weightedChoice` to avoid confusion. Params: rnd = (optional) random number generator to use; if not @@ -3055,6 +3111,9 @@ if (isNumeric!Num) /// @safe unittest { + auto d6 = 1 + dice(1, 1, 1, 1, 1, 1); // fair dice roll + auto d6b = 1 + dice(2, 1, 1, 1, 1, 1); // double the chance to roll '1' + auto x = dice(0.5, 0.5); // x is 0 or 1 in equal proportions auto y = dice(50, 50); // y is 0 or 1 in equal proportions auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 20% of the time, diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index 8614dc96901..a1fe962906b 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -1132,10 +1132,9 @@ each item is inferred from the size and type of the input array, respectively. Returns: The slice of `buffer` containing the data that was actually read. This will be shorter than `buffer` if EOF was reached before the buffer -could be filled. +could be filled. If the buffer is empty, it will be returned. -Throws: `Exception` if `buffer` is empty. - `ErrnoException` if the file is not opened or the call to `fread` fails. +Throws: `ErrnoException` if the file is not opened or the call to `fread` fails. `rawRead` always reads in binary mode on Windows. */ @@ -1144,7 +1143,7 @@ Throws: `Exception` if `buffer` is empty. import std.exception : enforce, errnoEnforce; if (!buffer.length) - throw new Exception("rawRead must take a non-empty buffer"); + return buffer; enforce(isOpen, "Attempting to read from an unopened file"); version (Windows) { @@ -1211,6 +1210,16 @@ Throws: `Exception` if `buffer` is empty. } } + // https://issues.dlang.org/show_bug.cgi?id=13893 + @system unittest + { + import std.exception : assertNotThrown; + + File f; + ubyte[0] u; + assertNotThrown(f.rawRead(u)); + } + /** Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file handle. The number of items to write and the size of each diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d index 1d375ef4d7e..160665c6a31 100644 --- a/libphobos/src/std/sumtype.d +++ b/libphobos/src/std/sumtype.d @@ -1941,79 +1941,8 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) auto ref matchImpl(SumTypes...)(auto ref SumTypes args) if (allSatisfy!(isSumType, SumTypes) && args.length > 0) { - enum typeCount(SumType) = SumType.Types.length; alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); - - /* A TagTuple represents a single possible set of tags that `args` - * could have at runtime. - * - * Because D does not allow a struct to be the controlling expression - * of a switch statement, we cannot dispatch on the TagTuple directly. - * Instead, we must map each TagTuple to a unique integer and generate - * a case label for each of those integers. - * - * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses - * the same technique that's used to map index tuples to memory offsets - * in a multidimensional static array. - * - * For example, when `args` consists of two SumTypes with two member - * types each, the TagTuples corresponding to each case label are: - * - * case 0: TagTuple([0, 0]) - * case 1: TagTuple([1, 0]) - * case 2: TagTuple([0, 1]) - * case 3: TagTuple([1, 1]) - * - * When there is only one argument, the caseId is equal to that - * argument's tag. - */ - static struct TagTuple - { - size_t[SumTypes.length] tags; - alias tags this; - - invariant - { - static foreach (i; 0 .. tags.length) - { - assert(tags[i] < SumTypes[i].Types.length, "Invalid tag"); - } - } - - this(ref const(SumTypes) args) - { - static foreach (i; 0 .. tags.length) - { - tags[i] = args[i].tag; - } - } - - static TagTuple fromCaseId(size_t caseId) - { - TagTuple result; - - // Most-significant to least-significant - static foreach_reverse (i; 0 .. result.length) - { - result[i] = caseId / stride!i; - caseId %= stride!i; - } - - return result; - } - - size_t toCaseId() - { - size_t result; - - static foreach (i; 0 .. tags.length) - { - result += tags[i] * stride!i; - } - - return result; - } - } + alias TagTuple = .TagTuple!(SumTypes); /* * A list of arguments to be passed to a handler needed for the case @@ -2149,6 +2078,81 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) } } +private enum typeCount(SumType) = SumType.Types.length; + +/* A TagTuple represents a single possible set of tags that `args` + * could have at runtime. + * + * Because D does not allow a struct to be the controlling expression + * of a switch statement, we cannot dispatch on the TagTuple directly. + * Instead, we must map each TagTuple to a unique integer and generate + * a case label for each of those integers. + * + * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses + * the same technique that's used to map index tuples to memory offsets + * in a multidimensional static array. + * + * For example, when `args` consists of two SumTypes with two member + * types each, the TagTuples corresponding to each case label are: + * + * case 0: TagTuple([0, 0]) + * case 1: TagTuple([1, 0]) + * case 2: TagTuple([0, 1]) + * case 3: TagTuple([1, 1]) + * + * When there is only one argument, the caseId is equal to that + * argument's tag. + */ +private struct TagTuple(SumTypes...) +{ + size_t[SumTypes.length] tags; + alias tags this; + + alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); + + invariant + { + static foreach (i; 0 .. tags.length) + { + assert(tags[i] < SumTypes[i].Types.length, "Invalid tag"); + } + } + + this(ref const(SumTypes) args) + { + static foreach (i; 0 .. tags.length) + { + tags[i] = args[i].tag; + } + } + + static TagTuple fromCaseId(size_t caseId) + { + TagTuple result; + + // Most-significant to least-significant + static foreach_reverse (i; 0 .. result.length) + { + result[i] = caseId / stride!i; + caseId %= stride!i; + } + + return result; + } + + size_t toCaseId() + { + size_t result; + + static foreach (i; 0 .. tags.length) + { + result += tags[i] * stride!i; + } + + return result; + } +} + // Matching @safe unittest { diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index 8a3e22f74a9..4ecfb1051d1 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -4905,8 +4905,14 @@ if (is(Interface == interface) && is(BaseClass == class)) // - try default first // - only on a failure run & return fallback enum fallback = q{ - scope (failure) return fallback.%1$s(args); - return default_.%1$s(args); + try + { + return default_.%1$s(args); + } + catch (Exception) + { + return fallback.%1$s(args); + } }.format(__traits(identifier, func)); } @@ -6589,15 +6595,11 @@ if (!is(T == class) && !(is(T == interface))) private enum enableGCScan = hasIndirections!T; } - // TODO remove pure when https://issues.dlang.org/show_bug.cgi?id=15862 has been fixed extern(C) private pure nothrow @nogc static { pragma(mangle, "free") void pureFree( void *ptr ); static if (enableGCScan) - { - pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null ); - pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p ); - } + import core.memory : GC; } /// `RefCounted` storage implementation. @@ -6637,7 +6639,7 @@ if (!is(T == class) && !(is(T == interface))) { import std.internal.memory : enforceCalloc; _store = cast(Impl*) enforceCalloc(1, Impl.sizeof); - pureGcAddRange(&_store._payload, T.sizeof); + GC.addRange(&_store._payload, T.sizeof); } else { @@ -6650,7 +6652,7 @@ if (!is(T == class) && !(is(T == interface))) { static if (enableGCScan) { - pureGcRemoveRange(&this._store._payload); + GC.removeRange(&this._store._payload); } pureFree(_store); _store = null; diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d index 98735ac1a88..e12a70cfe80 100644 --- a/libphobos/src/std/uni/package.d +++ b/libphobos/src/std/uni/package.d @@ -7032,9 +7032,7 @@ template genericDecodeGrapheme(bool getValue) case RI: if (isRegionalIndicator(ch)) mixin(eat); - else - goto L_End_Extend; - break; + goto L_End_Extend; case L: if (isHangL(ch)) mixin(eat); @@ -7166,6 +7164,10 @@ if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) s = "\u11A8\u0308\uAC01"; assert(equal(decodeGrapheme(s)[], "\u11A8\u0308")); assert(equal(decodeGrapheme(s)[], "\uAC01")); + + // Two Union Jacks of the Great Britain + s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + assert(equal(decodeGrapheme(s)[], "\U0001F1EC\U0001F1E7")); } /++ |