From b09ef4c1ae2f4eb02750f7c5ea0e8a6a3d482e08 Mon Sep 17 00:00:00 2001 From: Nathan Sashihara <21227491+n8sh@users.noreply.github.com> Date: Wed, 25 Nov 2020 20:26:35 -0800 Subject: [PATCH 01/21] Change private function BigInt.isNegative to an alias --- std/bigint.d | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/std/bigint.d b/std/bigint.d index 95829cdb2..4b640220a 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -1286,7 +1286,7 @@ public: } assert(buff.length > 0, "Invalid buffer length"); - char signChar = isNegative() ? '-' : 0; + char signChar = isNegative ? '-' : 0; auto minw = buff.length + (signChar ? 1 : 0); if (!hex && !signChar && (f.width == 0 || minw < f.width)) @@ -1459,10 +1459,7 @@ private: { return data.isZero(); } - bool isNegative() pure const nothrow @nogc @safe - { - return sign; - } + alias isNegative = sign; // Generate a runtime error if division by zero occurs void checkDivByZero() pure const nothrow @safe @@ -2160,23 +2157,23 @@ unittest { auto x = BigInt(-3); x %= 3; - assert(!x.isNegative()); - assert(x.isZero()); + assert(!x.isNegative); + assert(x.isZero); x = BigInt(-3); x %= cast(ushort) 3; - assert(!x.isNegative()); - assert(x.isZero()); + assert(!x.isNegative); + assert(x.isZero); x = BigInt(-3); x %= 3L; - assert(!x.isNegative()); - assert(x.isZero()); + assert(!x.isNegative); + assert(x.isZero); x = BigInt(3); x %= -3; - assert(!x.isNegative()); - assert(x.isZero()); + assert(!x.isNegative); + assert(x.isZero); } // https://issues.dlang.org/show_bug.cgi?id=15678 From b3978d0a2f5b22de371f451ad62a395a49cad716 Mon Sep 17 00:00:00 2001 From: Nathan Sashihara <21227491+n8sh@users.noreply.github.com> Date: Thu, 26 Nov 2020 09:06:12 -0800 Subject: [PATCH 02/21] Fix Issue 21430 - Add `const` to front, save, & length properties of range returned by std.bitmanip.bitsSet --- std/bitmanip.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/bitmanip.d b/std/bitmanip.d index 2b41c5aac..508bc3a8d 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -4537,7 +4537,7 @@ private struct BitsSet(T) _index = startIndex + trailingZerosCount; } - @property size_t front() + @property size_t front() const { return _index; } @@ -4562,12 +4562,12 @@ private struct BitsSet(T) _index += trailingZerosCount + 1; } - @property auto save() + @property BitsSet save() const { return this; } - @property size_t length() + @property size_t length() const { return countBitsSet(_value); } From b6a9cd8c649b0c13a39674f74b341655b5964514 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Sat, 28 Nov 2020 19:13:11 +0100 Subject: [PATCH 03/21] Fix broken link in minElement doc --- std/algorithm/searching.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 3fbfef6db..f671b98de 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -3577,7 +3577,7 @@ Note: --- If you want to get NaN as a result if a NaN is present in the range, - you can use $(REF fold, std.algorithm,iteration) and $(REF isNaN, std,math): + you can use $(REF fold, std,algorithm,iteration) and $(REF isNaN, std,math): --- .fold!((a,b)=>a.isNaN || b.isNaN ? real.nan : a < b ? a : b); From 28f8de0112fbeb1aa7a5fd838662a3baaa204f13 Mon Sep 17 00:00:00 2001 From: Nathan Sashihara <21227491+n8sh@users.noreply.github.com> Date: Thu, 19 Nov 2020 14:49:20 -0800 Subject: [PATCH 04/21] Fix Issue 21408 - Make std.math.nextUp and nextDown and nextafter work in CTFE for extended-precision real --- std/math.d | 76 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/std/math.d b/std/math.d index d93fd9f57..38c4502cf 100644 --- a/std/math.d +++ b/std/math.d @@ -6883,6 +6883,43 @@ debug(UnitTest) real nextUp(real x) @trusted pure nothrow @nogc { alias F = floatTraits!(real); + static if (F.realFormat != RealFormat.ieeeDouble) + { + if (__ctfe) + { + if (x == -real.infinity) + return -real.max; + if (!(x < real.infinity)) // Infinity or NaN. + return x; + real delta; + // Start with a decent estimate of delta. + if (x <= 0x1.ffffffffffffep+1023 && x >= -double.max) + { + const double d = cast(double) x; + delta = (cast(real) nextUp(d) - cast(real) d) * 0x1p-11L; + while (x + (delta * 0x1p-100L) > x) + delta *= 0x1p-100L; + } + else + { + delta = 0x1p960L; + while (!(x + delta > x) && delta < real.max * 0x1p-100L) + delta *= 0x1p100L; + } + if (x + delta > x) + { + while (x + (delta / 2) > x) + delta /= 2; + } + else + { + do { delta += delta; } while (!(x + delta > x)); + } + if (x < 0 && x + delta == 0) + return -0.0L; + return x + delta; + } + } static if (F.realFormat == RealFormat.ieeeDouble) { return nextUp(cast(double) x); @@ -7082,15 +7119,21 @@ float nextDown(float x) @safe pure nothrow @nogc @safe pure nothrow @nogc unittest { - static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended || + floatTraits!(real).realFormat == RealFormat.ieeeDouble || + floatTraits!(real).realFormat == RealFormat.ieeeExtended53 || + floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) { - - // Tests for 80-bit reals + // Tests for reals assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC))); + //static assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC))); // negative numbers assert( nextUp(-real.infinity) == -real.max ); assert( nextUp(-1.0L-real.epsilon) == -1.0 ); assert( nextUp(-2.0L) == -2.0 + real.epsilon); + static assert( nextUp(-real.infinity) == -real.max ); + static assert( nextUp(-1.0L-real.epsilon) == -1.0 ); + static assert( nextUp(-2.0L) == -2.0 + real.epsilon); // subnormals and zero assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) ); assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) ); @@ -7099,11 +7142,24 @@ float nextDown(float x) @safe pure nothrow @nogc assert( nextUp(0.0L) == real.min_normal*real.epsilon ); assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal ); assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) ); + static assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) ); + static assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) ); + static assert( -0.0L is nextUp(-real.min_normal*real.epsilon) ); + static assert( nextUp(-0.0L) == real.min_normal*real.epsilon ); + static assert( nextUp(0.0L) == real.min_normal*real.epsilon ); + static assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal ); + static assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) ); // positive numbers assert( nextUp(1.0L) == 1.0 + real.epsilon ); assert( nextUp(2.0L-real.epsilon) == 2.0 ); assert( nextUp(real.max) == real.infinity ); assert( nextUp(real.infinity)==real.infinity ); + static assert( nextUp(1.0L) == 1.0 + real.epsilon ); + static assert( nextUp(2.0L-real.epsilon) == 2.0 ); + static assert( nextUp(real.max) == real.infinity ); + static assert( nextUp(real.infinity)==real.infinity ); + // ctfe near double.max boundary + static assert(nextUp(nextDown(cast(real) double.max)) == cast(real) double.max); } double n = NaN(0xABC); @@ -7174,10 +7230,10 @@ float nextDown(float x) @safe pure nothrow @nogc static assert(nextUp(1.0f) == 1.0f+float.epsilon); static assert(nextUp(-0.0f) == float.min_normal*float.epsilon); static assert(nextUp(float.infinity)==float.infinity); - //static assert(nextDown(1.0L+real.epsilon)==1.0); + static assert(nextDown(1.0L+real.epsilon)==1.0); static assert(nextDown(1.0+double.epsilon)==1.0); static assert(nextDown(1.0f+float.epsilon)==1.0); - //static assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0); + static assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0); } @@ -7253,15 +7309,15 @@ T nextafter(T)(const T x, const T y) @safe pure nothrow @nogc enum real c = 3; static assert(is(typeof(nextafter(c, c)) == real)); - //static assert(nextafter(c, c.infinity) > c); + static assert(nextafter(c, c.infinity) > c); + static assert(isNaN(nextafter(c, c.nan))); + static assert(isNaN(nextafter(c.nan, c))); - enum real negZero = nextafter(+0.0L, -0.0L); // specially CTFEable + enum real negZero = nextafter(+0.0L, -0.0L); static assert(negZero == -0.0L); static assert(signbit(negZero)); - static assert(nextafter(c, c) == c); // ditto - static assert(isNaN(nextafter(c, c.nan))); // ditto - static assert(isNaN(nextafter(c.nan, c))); // ditto + static assert(nextafter(c, c) == c); } //real nexttoward(real x, real y) { return core.stdc.math.nexttowardl(x, y); } From 3cae1be7f72705d777d11fa5dc533e12bae451a4 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Fri, 26 Apr 2019 17:42:23 +0200 Subject: [PATCH 05/21] std.datetime.systime: Skip unsupported clocks in unittests For the same reasons why we can't test all clocks in core.time. --- std/datetime/systime.d | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 91e60f962..e9f07b5f7 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -113,6 +113,16 @@ version (StdUnittest) initializeTests(); } +version (unittest) private bool clockSupported(ClockType c) +{ + // Skip unsupported clocks on older linux kernels, assume that only + // CLOCK_MONOTONIC and CLOCK_REALTIME exist, as that is the lowest + // common denominator supported by all versions of Linux pre-2.6.12. + version (Linux_Pre_2639) + return c == ClockType.normal || c == ClockType.precise; + else + return true; +} /++ Effectively a namespace to make it clear that the methods it contains are @@ -168,10 +178,13 @@ public: import std.meta : AliasSeq; static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) {{ - auto value1 = Clock.currTime!ct; - auto value2 = Clock.currTime!ct(UTC()); - assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); - assert(abs(value1 - value2) <= seconds(2), format("ClockType.%s", ct)); + static if (clockSupported(ct)) + { + auto value1 = Clock.currTime!ct; + auto value2 = Clock.currTime!ct(UTC()); + assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); + assert(abs(value1 - value2) <= seconds(2), format("ClockType.%s", ct)); + } }} } @@ -377,10 +390,13 @@ public: static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) {{ - auto value1 = Clock.currStdTime!ct; - auto value2 = Clock.currStdTime!ct; - assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); - assert(abs(value1 - value2) <= limit); + static if (clockSupported(ct)) + { + auto value1 = Clock.currStdTime!ct; + auto value2 = Clock.currStdTime!ct; + assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct)); + assert(abs(value1 - value2) <= limit); + } }} } From a2c407b42c9ff1b799af5d3c75614f598fde8f74 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Tue, 1 Dec 2020 10:46:14 +0100 Subject: [PATCH 06/21] std.stdio: Deprecate extern(C) getline The publicly available `extern(C)` binding for `getline` in `std.stdio` has been deprecated. Any code that still needs it can import the symbol from `core.sys.posix.stdio` in druntime instead. --- changelog/deprecate-stdio-bindings.dd | 5 +++++ std/stdio.d | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 changelog/deprecate-stdio-bindings.dd diff --git a/changelog/deprecate-stdio-bindings.dd b/changelog/deprecate-stdio-bindings.dd new file mode 100644 index 000000000..5b9a1e26a --- /dev/null +++ b/changelog/deprecate-stdio-bindings.dd @@ -0,0 +1,5 @@ +Deprecate `std.stdio.getline` + +The publicly available `extern(C)` binding for `getline` in `std.stdio` has +been deprecated. Any code that still needs it can import the symbol from +`core.sys.posix.stdio` in druntime instead. diff --git a/std/stdio.d b/std/stdio.d index ae262be6e..6cfdc7107 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -264,7 +264,10 @@ else version (HAS_GETDELIM) extern(C) nothrow @nogc { ptrdiff_t getdelim(char**, size_t*, int, FILE*); + + // @@@DEPRECATED_2.104@@@ // getline() always comes together with getdelim() + deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getline instead.") ptrdiff_t getline(char**, size_t*, FILE*); } From 9c7974a3dbdf5b4a0d4adbfa0e8f982f37e2bf94 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Tue, 1 Dec 2020 11:02:57 +0100 Subject: [PATCH 07/21] std.stdio: Merge readlnImpl implementations into one function --- std/stdio.d | 566 ++++++++++++++++++++++++++-------------------------- 1 file changed, 281 insertions(+), 285 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 6cfdc7107..7709847b0 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -5270,59 +5270,142 @@ private struct ReadlnAppender } // Private implementation of readln -version (DIGITAL_MARS_STDIO) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/) +private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) { - FLOCK(fps); - scope(exit) FUNLOCK(fps); + version (DIGITAL_MARS_STDIO) + { + FLOCK(fps); + scope(exit) FUNLOCK(fps); - /* Since fps is now locked, we can create an "unshared" version - * of fp. - */ - auto fp = cast(_iobuf*) fps; - - ReadlnAppender app; - app.initialize(buf); - - if (__fhnd_info[fp._file] & FHND_WCHAR) - { /* Stream is in wide characters. - * Read them and convert to chars. + /* Since fps is now locked, we can create an "unshared" version + * of fp. */ - static assert(wchar_t.sizeof == 2); - for (int c = void; (c = FGETWC(fp)) != -1; ) + auto fp = cast(_iobuf*) fps; + + ReadlnAppender app; + app.initialize(buf); + + if (__fhnd_info[fp._file] & FHND_WCHAR) + { /* Stream is in wide characters. + * Read them and convert to chars. + */ + static assert(wchar_t.sizeof == 2); + for (int c = void; (c = FGETWC(fp)) != -1; ) + { + if ((c & ~0x7F) == 0) + { + app.putchar(cast(char) c); + if (c == terminator) + break; + } + else + { + if (c >= 0xD800 && c <= 0xDBFF) + { + int c2 = void; + if ((c2 = FGETWC(fp)) != -1 || + c2 < 0xDC00 && c2 > 0xDFFF) + { + StdioException("unpaired UTF-16 surrogate"); + } + c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + } + app.putdchar(cast(dchar) c); + } + } + if (ferror(fps)) + StdioException(); + } + + else if (fp._flag & _IONBF) { - if ((c & ~0x7F) == 0) + /* Use this for unbuffered I/O, when running + * across buffer boundaries, or for any but the common + * cases. + */ + L1: + int c; + while ((c = FGETC(fp)) != -1) { app.putchar(cast(char) c); if (c == terminator) - break; + { + buf = app.data; + return buf.length; + } + + } + + if (ferror(fps)) + StdioException(); + } + else + { + int u = fp._cnt; + char* p = fp._ptr; + int i; + if (fp._flag & _IOTRAN) + { /* Translated mode ignores \r and treats ^Z as end-of-file + */ + char c; + while (1) + { + if (i == u) // if end of buffer + goto L1; // give up + c = p[i]; + i++; + if (c != '\r') + { + if (c == terminator) + break; + if (c != 0x1A) + continue; + goto L1; + } + else + { if (i != u && p[i] == terminator) + break; + goto L1; + } + } + app.putonly(p[0 .. i]); + app.buf[i - 1] = cast(char) terminator; + if (terminator == '\n' && c == '\r') + i++; } else { - if (c >= 0xD800 && c <= 0xDBFF) + while (1) { - int c2 = void; - if ((c2 = FGETWC(fp)) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) - { - StdioException("unpaired UTF-16 surrogate"); - } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + if (i == u) // if end of buffer + goto L1; // give up + auto c = p[i]; + i++; + if (c == terminator) + break; } - app.putdchar(cast(dchar) c); + app.putonly(p[0 .. i]); } + fp._cnt -= i; + fp._ptr += i; } - if (ferror(fps)) - StdioException(); - } - else if (fp._flag & _IONBF) + buf = app.data; + return buf.length; + } + else version (MICROSOFT_STDIO) { - /* Use this for unbuffered I/O, when running - * across buffer boundaries, or for any but the common - * cases. + FLOCK(fps); + scope(exit) FUNLOCK(fps); + + /* Since fps is now locked, we can create an "unshared" version + * of fp. */ - L1: + auto fp = cast(_iobuf*) fps; + + ReadlnAppender app; + app.initialize(buf); + int c; while ((c = FGETC(fp)) != -1) { @@ -5337,295 +5420,208 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie if (ferror(fps)) StdioException(); + buf = app.data; + return buf.length; } - else + else version (HAS_GETDELIM) { - int u = fp._cnt; - char* p = fp._ptr; - int i; - if (fp._flag & _IOTRAN) - { /* Translated mode ignores \r and treats ^Z as end-of-file + import core.stdc.stdlib : free; + import core.stdc.wchar_ : fwide; + + if (orientation == File.Orientation.wide) + { + /* Stream is in wide characters. + * Read them and convert to chars. */ - char c; - while (1) + FLOCK(fps); + scope(exit) FUNLOCK(fps); + auto fp = cast(_iobuf*) fps; + version (Windows) { - if (i == u) // if end of buffer - goto L1; // give up - c = p[i]; - i++; - if (c != '\r') + buf.length = 0; + for (int c = void; (c = FGETWC(fp)) != -1; ) { + if ((c & ~0x7F) == 0) + { buf ~= c; + if (c == terminator) + break; + } + else + { + if (c >= 0xD800 && c <= 0xDBFF) + { + int c2 = void; + if ((c2 = FGETWC(fp)) != -1 || + c2 < 0xDC00 && c2 > 0xDFFF) + { + StdioException("unpaired UTF-16 surrogate"); + } + c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + } + import std.utf : encode; + encode(buf, c); + } + } + if (ferror(fp)) + StdioException(); + return buf.length; + } + else version (Posix) + { + buf.length = 0; + for (int c; (c = FGETWC(fp)) != -1; ) + { + import std.utf : encode; + + if ((c & ~0x7F) == 0) + buf ~= cast(char) c; + else + encode(buf, cast(dchar) c); if (c == terminator) break; - if (c != 0x1A) - continue; - goto L1; - } - else - { if (i != u && p[i] == terminator) - break; - goto L1; } + if (ferror(fps)) + StdioException(); + return buf.length; } - app.putonly(p[0 .. i]); - app.buf[i - 1] = cast(char) terminator; - if (terminator == '\n' && c == '\r') - i++; + else + { + static assert(0); + } + } + + static char *lineptr = null; + static size_t n = 0; + scope(exit) + { + if (n > 128 * 1024) + { + // Bound memory used by readln + free(lineptr); + lineptr = null; + n = 0; + } + } + + auto s = getdelim(&lineptr, &n, terminator, fps); + if (s < 0) + { + if (ferror(fps)) + StdioException(); + buf.length = 0; // end of file + return 0; + } + + if (s <= buf.length) + { + buf = buf[0 .. s]; + buf[] = lineptr[0 .. s]; } else { - while (1) - { - if (i == u) // if end of buffer - goto L1; // give up - auto c = p[i]; - i++; - if (c == terminator) - break; - } - app.putonly(p[0 .. i]); + buf = lineptr[0 .. s].dup; } - fp._cnt -= i; - fp._ptr += i; + return s; } - - buf = app.data; - return buf.length; -} - -version (MICROSOFT_STDIO) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/) -{ - FLOCK(fps); - scope(exit) FUNLOCK(fps); - - /* Since fps is now locked, we can create an "unshared" version - * of fp. - */ - auto fp = cast(_iobuf*) fps; - - ReadlnAppender app; - app.initialize(buf); - - int c; - while ((c = FGETC(fp)) != -1) + else // version (NO_GETDELIM) { - app.putchar(cast(char) c); - if (c == terminator) - { - buf = app.data; - return buf.length; - } + import core.stdc.wchar_ : fwide; - } - - if (ferror(fps)) - StdioException(); - buf = app.data; - return buf.length; -} - -version (HAS_GETDELIM) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) -{ - import core.stdc.stdlib : free; - import core.stdc.wchar_ : fwide; - - if (orientation == File.Orientation.wide) - { - /* Stream is in wide characters. - * Read them and convert to chars. - */ FLOCK(fps); scope(exit) FUNLOCK(fps); auto fp = cast(_iobuf*) fps; - version (Windows) + if (orientation == File.Orientation.wide) { - buf.length = 0; - for (int c = void; (c = FGETWC(fp)) != -1; ) + /* Stream is in wide characters. + * Read them and convert to chars. + */ + version (Windows) { - if ((c & ~0x7F) == 0) - { buf ~= c; - if (c == terminator) - break; - } - else + buf.length = 0; + for (int c; (c = FGETWC(fp)) != -1; ) { - if (c >= 0xD800 && c <= 0xDBFF) - { - int c2 = void; - if ((c2 = FGETWC(fp)) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) - { - StdioException("unpaired UTF-16 surrogate"); - } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + if ((c & ~0x7F) == 0) + { buf ~= c; + if (c == terminator) + break; + } + else + { + if (c >= 0xD800 && c <= 0xDBFF) + { + int c2 = void; + if ((c2 = FGETWC(fp)) != -1 || + c2 < 0xDC00 && c2 > 0xDFFF) + { + StdioException("unpaired UTF-16 surrogate"); + } + c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + } + import std.utf : encode; + encode(buf, c); } - import std.utf : encode; - encode(buf, c); } + if (ferror(fp)) + StdioException(); + return buf.length; } - if (ferror(fp)) - StdioException(); - return buf.length; - } - else version (Posix) - { - buf.length = 0; - for (int c; (c = FGETWC(fp)) != -1; ) + else version (Posix) { import std.utf : encode; - - if ((c & ~0x7F) == 0) - buf ~= cast(char) c; - else - encode(buf, cast(dchar) c); - if (c == terminator) - break; - } - if (ferror(fps)) - StdioException(); - return buf.length; - } - else - { - static assert(0); - } - } - - static char *lineptr = null; - static size_t n = 0; - scope(exit) - { - if (n > 128 * 1024) - { - // Bound memory used by readln - free(lineptr); - lineptr = null; - n = 0; - } - } - - auto s = getdelim(&lineptr, &n, terminator, fps); - if (s < 0) - { - if (ferror(fps)) - StdioException(); - buf.length = 0; // end of file - return 0; - } - - if (s <= buf.length) - { - buf = buf[0 .. s]; - buf[] = lineptr[0 .. s]; - } - else - { - buf = lineptr[0 .. s].dup; - } - return s; -} - -version (NO_GETDELIM) -private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) -{ - import core.stdc.wchar_ : fwide; - - FLOCK(fps); - scope(exit) FUNLOCK(fps); - auto fp = cast(_iobuf*) fps; - if (orientation == File.Orientation.wide) - { - /* Stream is in wide characters. - * Read them and convert to chars. - */ - version (Windows) - { - buf.length = 0; - for (int c; (c = FGETWC(fp)) != -1; ) - { - if ((c & ~0x7F) == 0) - { buf ~= c; + buf.length = 0; + for (int c; (c = FGETWC(fp)) != -1; ) + { + if ((c & ~0x7F) == 0) + buf ~= cast(char) c; + else + encode(buf, cast(dchar) c); if (c == terminator) break; } - else - { - if (c >= 0xD800 && c <= 0xDBFF) - { - int c2 = void; - if ((c2 = FGETWC(fp)) != -1 || - c2 < 0xDC00 && c2 > 0xDFFF) - { - StdioException("unpaired UTF-16 surrogate"); - } - c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); - } - import std.utf : encode; - encode(buf, c); - } + if (ferror(fps)) + StdioException(); + return buf.length; } - if (ferror(fp)) - StdioException(); - return buf.length; - } - else version (Posix) - { - import std.utf : encode; - buf.length = 0; - for (int c; (c = FGETWC(fp)) != -1; ) + else { - if ((c & ~0x7F) == 0) - buf ~= cast(char) c; - else - encode(buf, cast(dchar) c); - if (c == terminator) - break; + static assert(0); } - if (ferror(fps)) - StdioException(); - return buf.length; } - else - { - static assert(0); - } - } - // Narrow stream - // First, fill the existing buffer - for (size_t bufPos = 0; bufPos < buf.length; ) - { - immutable c = FGETC(fp); - if (c == -1) + // Narrow stream + // First, fill the existing buffer + for (size_t bufPos = 0; bufPos < buf.length; ) { - buf.length = bufPos; - goto endGame; + immutable c = FGETC(fp); + if (c == -1) + { + buf.length = bufPos; + goto endGame; + } + buf[bufPos++] = cast(char) c; + if (c == terminator) + { + // No need to test for errors in file + buf.length = bufPos; + return bufPos; + } } - buf[bufPos++] = cast(char) c; - if (c == terminator) + // Then, append to it + for (int c; (c = FGETC(fp)) != -1; ) { - // No need to test for errors in file - buf.length = bufPos; - return bufPos; + buf ~= cast(char) c; + if (c == terminator) + { + // No need to test for errors in file + return buf.length; + } } - } - // Then, append to it - for (int c; (c = FGETC(fp)) != -1; ) - { - buf ~= cast(char) c; - if (c == terminator) - { - // No need to test for errors in file - return buf.length; - } - } - endGame: - if (ferror(fps)) - StdioException(); - return buf.length; + endGame: + if (ferror(fps)) + StdioException(); + return buf.length; + } } @system unittest From 11f075500e53f9e092da2883e6af0626ce1a17ee Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Tue, 1 Dec 2020 11:31:06 +0100 Subject: [PATCH 08/21] std.stdio: Replace version(HAS_GETDELIM) with __traits(compiles, getdelim) --- std/stdio.d | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 7709847b0..ed1a4226b 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -37,69 +37,55 @@ else version (CRuntime_DigitalMars) // Specific to the way Digital Mars C does stdio version = DIGITAL_MARS_STDIO; } - -version (CRuntime_Glibc) +else version (CRuntime_Glibc) { // Specific to the way Gnu C does stdio version = GCC_IO; - version = HAS_GETDELIM; } else version (CRuntime_Bionic) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (CRuntime_Musl) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (CRuntime_UClibc) { // uClibc supports GCC IO version = GCC_IO; - version = HAS_GETDELIM; } - -version (OSX) +else version (OSX) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (iOS) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (TVOS) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (WatchOS) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (FreeBSD) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (NetBSD) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (DragonFlyBSD) { version = GENERIC_IO; - version = HAS_GETDELIM; } else version (Solaris) { version = GENERIC_IO; - version = NO_GETDELIM; } // Character type used for operating system filesystem APIs @@ -125,6 +111,11 @@ version (Windows) import core.sys.windows.basetsd : HANDLE; } +version (Posix) +{ + static import core.sys.posix.stdio; // getdelim +} + version (DIGITAL_MARS_STDIO) { extern (C) @@ -261,14 +252,17 @@ else static assert(0, "unsupported C I/O system"); } -version (HAS_GETDELIM) extern(C) nothrow @nogc +static if (__traits(compiles, core.sys.posix.stdio.getdelim)) { - ptrdiff_t getdelim(char**, size_t*, int, FILE*); + extern(C) nothrow @nogc + { + ptrdiff_t getdelim(char**, size_t*, int, FILE*); - // @@@DEPRECATED_2.104@@@ - // getline() always comes together with getdelim() - deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getline instead.") - ptrdiff_t getline(char**, size_t*, FILE*); + // @@@DEPRECATED_2.104@@@ + // getline() always comes together with getdelim() + deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getline instead.") + ptrdiff_t getline(char**, size_t*, FILE*); + } } //------------------------------------------------------------------------------ @@ -5423,7 +5417,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie buf = app.data; return buf.length; } - else version (HAS_GETDELIM) + else static if (__traits(compiles, core.sys.posix.stdio.getdelim)) { import core.stdc.stdlib : free; import core.stdc.wchar_ : fwide; From b3edd8729a512d3bbe35d514d429865e58bf9c49 Mon Sep 17 00:00:00 2001 From: Nathan Sashihara <21227491+n8sh@users.noreply.github.com> Date: Tue, 1 Dec 2020 08:07:39 -0800 Subject: [PATCH 09/21] Avoid quadratic template expansion in std.range.zip(Ranges).length --- std/range/package.d | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index c942161ae..3eed0927d 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -4718,7 +4718,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges)) static foreach (i, Range; Ranges) static if (hasLength!Range) { - static if (!anySatisfy!(hasLength, Ranges[0 .. i])) + static if (!is(typeof(minLen) == size_t)) size_t minLen = ranges[i].length; else {{ @@ -5077,29 +5077,20 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges)) { @property size_t length() { - static if (allKnownSameLength) - { - static foreach (i, Range; Ranges) + static foreach (i, Range; Ranges) + { + static if (hasLength!Range) { - static if (hasLength!Range && !anySatisfy!(hasLength, Ranges[0 .. i])) - return ranges[i].length; + static if (!is(typeof(minLen) == size_t)) + size_t minLen = ranges[i].length; + else static if (!allKnownSameLength) + {{ + const x = ranges[i].length; + if (x < minLen) minLen = x; + }} } } - else - { - static foreach (i, Range; Ranges) - static if (hasLength!Range) - { - static if (!anySatisfy!(hasLength, Ranges[0 .. i])) - size_t minLen = ranges[i].length; - else - {{ - const x = ranges[i].length; - if (x < minLen) minLen = x; - }} - } - return minLen; - } + return minLen; } alias opDollar = length; From f78fd811f0c34f426b638bb3cf86e0221d8bbeec Mon Sep 17 00:00:00 2001 From: Nathan Sashihara <21227491+n8sh@users.noreply.github.com> Date: Tue, 1 Dec 2020 08:31:39 -0800 Subject: [PATCH 10/21] Remove some unnecessary undocumented explicit ctors in std.range --- std/range/package.d | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 3eed0927d..9520657da 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -928,14 +928,6 @@ if (Ranges.length > 0 && // TODO: use a vtable (or more) instead of linear iteration public: - this(R input) - { - foreach (i, v; input) - { - source[i] = v; - } - } - import std.meta : anySatisfy; static if (anySatisfy!(isInfinite, R)) @@ -4891,15 +4883,6 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges)) private Ranges ranges; alias ElementType = Tuple!(staticMap!(.ElementType, Ranges)); - /+ - Builds an object. Usually this is invoked indirectly by using the - $(LREF zip) function. - +/ - this(Ranges rs) - { - ranges[] = rs[]; - } - /+ Returns `true` if the range is at end. +/ @@ -7716,12 +7699,6 @@ struct Indexed(Source, Indices) if (isRandomAccessRange!Source && isInputRange!Indices && is(typeof(Source.init[ElementType!(Indices).init]))) { - this(Source source, Indices indices) - { - this._source = source; - this._indices = indices; - } - /// Range primitives @property auto ref front() { From 2db474be28f1724af8093b04f896cf7ae33411fb Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Tue, 1 Dec 2020 15:46:05 +0100 Subject: [PATCH 11/21] std.stdio: Deprecate extern(C) getdelim The publicly available `extern(C)` binding for `getdelim` in `std.stdio` has been deprecated. Any code that still needs it can import the symbol from `core.sys.posix.stdio` in druntime instead. --- changelog/deprecate-stdio-bindings.dd | 8 ++++---- std/stdio.d | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/changelog/deprecate-stdio-bindings.dd b/changelog/deprecate-stdio-bindings.dd index 5b9a1e26a..c686616e8 100644 --- a/changelog/deprecate-stdio-bindings.dd +++ b/changelog/deprecate-stdio-bindings.dd @@ -1,5 +1,5 @@ -Deprecate `std.stdio.getline` +Deprecate `std.stdio.getdelim` and `std.stdio.getline` -The publicly available `extern(C)` binding for `getline` in `std.stdio` has -been deprecated. Any code that still needs it can import the symbol from -`core.sys.posix.stdio` in druntime instead. +The publicly available `extern(C)` bindings for `getdelim` and `getline` in +`std.stdio` have been deprecated. Any code that still needs it can import the +symbol from `core.sys.posix.stdio` in druntime instead. diff --git a/std/stdio.d b/std/stdio.d index ed1a4226b..29813a72e 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -256,6 +256,8 @@ static if (__traits(compiles, core.sys.posix.stdio.getdelim)) { extern(C) nothrow @nogc { + // @@@DEPRECATED_2.104@@@ + deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getline instead.") ptrdiff_t getdelim(char**, size_t*, int, FILE*); // @@@DEPRECATED_2.104@@@ @@ -5497,7 +5499,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie } } - auto s = getdelim(&lineptr, &n, terminator, fps); + auto s = core.sys.posix.stdio.getdelim(&lineptr, &n, terminator, fps); if (s < 0) { if (ferror(fps)) From 2c0660141748a13637ff473cbb7b0d52eb1c44db Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Wed, 2 Dec 2020 12:40:26 +0100 Subject: [PATCH 12/21] std.stdio: Fix typo in getdelim deprecation message --- std/stdio.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/stdio.d b/std/stdio.d index 29813a72e..eebfee3c1 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -257,7 +257,7 @@ static if (__traits(compiles, core.sys.posix.stdio.getdelim)) extern(C) nothrow @nogc { // @@@DEPRECATED_2.104@@@ - deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getline instead.") + deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getdelim instead.") ptrdiff_t getdelim(char**, size_t*, int, FILE*); // @@@DEPRECATED_2.104@@@ From 61a2fdd8f587f8d62c90b9cd24d25cc9c9fc1412 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Fri, 23 Oct 2020 22:39:11 +0200 Subject: [PATCH 13/21] Add some conversions to JSONValue.get!T --- std/json.d | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/std/json.d b/std/json.d index e1406d960..550a2ec2b 100644 --- a/std/json.d +++ b/std/json.d @@ -395,11 +395,31 @@ struct JSONValue } else static if (__traits(isUnsigned, T)) { - return cast(T) uinteger; + switch (type) + { + case JSONType.uinteger: + return cast(T) uinteger; + case JSONType.integer: + auto v = integer; + enforce!JSONException(v >= 0, "JSONValue is a negative number"); + return cast(T) v; + default: + throw new JSONException("JSONValue is not a number type"); + } } else static if (isSigned!T) { - return cast(T) integer; + switch (type) + { + case JSONType.uinteger: + auto v = integer; + enforce!JSONException(v > long.max, "JSONValue positive overflow"); + return cast(T) v; + case JSONType.integer: + return cast(T) integer; + default: + throw new JSONException("JSONValue is not a number type"); + } } else { @@ -427,7 +447,9 @@ struct JSONValue "c": "text", "d": true, "e": [1, 2, 3], - "f": { "a": 1 } + "f": { "a": 1 }, + "g": -45, + "h": ` ~ ulong.max.to!string ~ `, }`; struct a { } @@ -435,6 +457,7 @@ struct JSONValue immutable json = parseJSON(s); assert(json["a"].get!double == 123.0); assert(json["a"].get!int == 123); + assert(json["a"].get!uint == 123); assert(json["b"].get!double == 3.1415); assertThrown(json["b"].get!int); assert(json["c"].get!string == "text"); @@ -445,6 +468,10 @@ struct JSONValue assertThrown(json["e"].get!float); assertThrown(json["d"].get!(JSONValue[string])); assertThrown(json["f"].get!(JSONValue[])); + assert(json["g"].get!int == -45); + assertThrown(json["g"].get!uint); + assert(json["h"].get!uint == uint.max); + assertThrown(json["h"].get!int); } private void assign(T)(T arg) From 54c56ecb09e766b636080bfc5844f43bacd260f3 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Sat, 24 Oct 2020 17:21:05 +0200 Subject: [PATCH 14/21] use std.conv.to to check for overflows --- std/json.d | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/std/json.d b/std/json.d index 550a2ec2b..1c7a53b72 100644 --- a/std/json.d +++ b/std/json.d @@ -368,9 +368,11 @@ struct JSONValue * A convenience getter that returns this `JSONValue` as the specified D type. * Note: only numeric, `bool`, `string`, `JSONValue[string]` and `JSONValue[]` types are accepted * Throws: `JSONException` if `T` cannot hold the contents of this `JSONValue` + * `ConvException` if there is a type overflow issue */ @property inout(T) get(T)() inout const pure @safe { + import std.conv : to; static if (is(immutable T == immutable string)) { return str; @@ -393,30 +395,14 @@ struct JSONValue throw new JSONException("JSONValue is not a number type"); } } - else static if (__traits(isUnsigned, T)) + else static if (isIntegral!T) { switch (type) { case JSONType.uinteger: - return cast(T) uinteger; + return uinteger.to!T; case JSONType.integer: - auto v = integer; - enforce!JSONException(v >= 0, "JSONValue is a negative number"); - return cast(T) v; - default: - throw new JSONException("JSONValue is not a number type"); - } - } - else static if (isSigned!T) - { - switch (type) - { - case JSONType.uinteger: - auto v = integer; - enforce!JSONException(v > long.max, "JSONValue positive overflow"); - return cast(T) v; - case JSONType.integer: - return cast(T) integer; + return integer.to!T; default: throw new JSONException("JSONValue is not a number type"); } @@ -440,6 +426,7 @@ struct JSONValue @safe unittest { import std.exception; + import std.conv; string s = `{ "a": 123, @@ -459,19 +446,20 @@ struct JSONValue assert(json["a"].get!int == 123); assert(json["a"].get!uint == 123); assert(json["b"].get!double == 3.1415); - assertThrown(json["b"].get!int); + assertThrown!JSONException(json["b"].get!int); assert(json["c"].get!string == "text"); assert(json["d"].get!bool == true); assertNotThrown(json["e"].get!(JSONValue[])); assertNotThrown(json["f"].get!(JSONValue[string])); static assert(!__traits(compiles, json["a"].get!a)); - assertThrown(json["e"].get!float); - assertThrown(json["d"].get!(JSONValue[string])); - assertThrown(json["f"].get!(JSONValue[])); + assertThrown!JSONException(json["e"].get!float); + assertThrown!JSONException(json["d"].get!(JSONValue[string])); + assertThrown!JSONException(json["f"].get!(JSONValue[])); assert(json["g"].get!int == -45); - assertThrown(json["g"].get!uint); - assert(json["h"].get!uint == uint.max); - assertThrown(json["h"].get!int); + assertThrown!ConvException(json["g"].get!uint); + assert(json["h"].get!ulong == ulong.max); + assertThrown!ConvException(json["h"].get!uint); + assertNotThrown(json["h"].get!float); } private void assign(T)(T arg) From de28a13d54e21966bb69a96311a7a47021ae5275 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Sun, 25 Oct 2020 12:54:05 +0100 Subject: [PATCH 15/21] Better wording & removed useless import --- std/json.d | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/std/json.d b/std/json.d index 1c7a53b72..c3e17ee66 100644 --- a/std/json.d +++ b/std/json.d @@ -368,11 +368,10 @@ struct JSONValue * A convenience getter that returns this `JSONValue` as the specified D type. * Note: only numeric, `bool`, `string`, `JSONValue[string]` and `JSONValue[]` types are accepted * Throws: `JSONException` if `T` cannot hold the contents of this `JSONValue` - * `ConvException` if there is a type overflow issue + * `ConvException` in case of integer overflow when converting to `T` */ @property inout(T) get(T)() inout const pure @safe { - import std.conv : to; static if (is(immutable T == immutable string)) { return str; @@ -404,7 +403,7 @@ struct JSONValue case JSONType.integer: return integer.to!T; default: - throw new JSONException("JSONValue is not a number type"); + throw new JSONException("JSONValue is not a an integral type"); } } else From 3df73cff4ae2dfdad7da826686d3b7c91bc858ac Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Sun, 25 Oct 2020 13:17:35 +0100 Subject: [PATCH 16/21] Added changelog --- changelog/jsonvalue-get-integer-conv.dd | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelog/jsonvalue-get-integer-conv.dd diff --git a/changelog/jsonvalue-get-integer-conv.dd b/changelog/jsonvalue-get-integer-conv.dd new file mode 100644 index 000000000..78fc46672 --- /dev/null +++ b/changelog/jsonvalue-get-integer-conv.dd @@ -0,0 +1,10 @@ +Add integer conversions in `JSONValue.get` + +`JSONValue.get` now allows to convert a stored `uinteger` or `integer` into any +signed or unsigned integer. The conversion is performed with `std.conv.to`, and +throws a `ConvException` in case of an integer overflow; + +------- +auto json = parseJSON(`{"a": 123}`); +writeln(json["a"].get!ubyte); +------- From d64b78e27d838110f2f27cc4e6a6bb6866bada25 Mon Sep 17 00:00:00 2001 From: "Q. F. Schroll" Date: Thu, 3 Dec 2020 22:33:45 +0100 Subject: [PATCH 17/21] fix Issue 21452 - isCallable erroneously returns false on function templates --- std/traits.d | 56 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/std/traits.d b/std/traits.d index d2d02e007..baca58e52 100644 --- a/std/traits.d +++ b/std/traits.d @@ -7402,36 +7402,74 @@ if (T.length == 1) Detect whether `T` is a callable object, which can be called with the function call operator `$(LPAREN)...$(RPAREN)`. */ -template isCallable(T...) -if (T.length == 1) +template isCallable(alias callable) { - static if (is(typeof(& T[0].opCall) == delegate)) + static if (is(typeof(&callable.opCall) == delegate)) // T is a object which has a member function opCall(). enum bool isCallable = true; - else static if (is(typeof(& T[0].opCall) V : V*) && is(V == function)) + else static if (is(typeof(&callable.opCall) V : V*) && is(V == function)) // T is a type which has a static member function opCall(). enum bool isCallable = true; + else static if (is(typeof(&callable!()))) + { + alias TemplateInstanceType = typeof(&callable!()); + enum bool isCallable = isCallable!TemplateInstanceType; + } else - enum bool isCallable = isSomeFunction!T; + { + enum bool isCallable = isSomeFunction!callable; + } } -/// +/// Functions, lambdas, and aggregate types with (static) opCall. @safe unittest { - interface I { real value() @property; } - struct S { static int opCall(int) { return 0; } } + void f() { } + int g(int x) { return x; } + + static assert( isCallable!f); + static assert( isCallable!g); + class C { int opCall(int) { return 0; } } auto c = new C; + struct S { static int opCall(int) { return 0; } } + interface I { real value() @property; } static assert( isCallable!c); - static assert( isCallable!S); static assert( isCallable!(c.opCall)); + static assert( isCallable!S); static assert( isCallable!(I.value)); static assert( isCallable!((int a) { return a; })); static assert(!isCallable!I); } +/// Templates +@safe unittest +{ + void f()() { } + T g(T = int)(T x) { return x; } + + static assert( isCallable!f); + static assert( isCallable!g); +} + +/// Overloaded functions and function templates. +@safe unittest +{ + static struct Wrapper + { + void f() { } + int f(int x) { return x; } + + void g()() { } + T g(T = int)(T x) { return x; } + } + + static assert(isCallable!(Wrapper.f)); + static assert(isCallable!(Wrapper.g)); +} + /** Detect whether `T` is an abstract function. From b20072dd78469b1298ce79d615901682026d9741 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Wed, 9 Dec 2020 17:04:22 +0100 Subject: [PATCH 18/21] CirrusCI: Use HOST_DMD instead of HOST_DC Using HOST_DC is deprecated in the makefile, and the CI triggers a message that can be misleading while trying to debug. --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 0aba24aa0..2ec46db23 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -17,7 +17,7 @@ environment: CIRRUS_CLONE_DEPTH: 1 # for ci.sh: MODEL: 64 - HOST_DC: dmd + HOST_DMD: dmd N: 4 OS_NAME: linux FULL_BUILD: false From 7746e007d26d21431c07e9f75d6966fa53925e98 Mon Sep 17 00:00:00 2001 From: Panke Date: Sat, 12 Dec 2020 13:59:20 +0100 Subject: [PATCH 19/21] ] in character classes must be escaped. see https://forum.dlang.org/post/vosqqfhrvddeekjopjjp@forum.dlang.org --- std/regex/package.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/std/regex/package.d b/std/regex/package.d index 60e3e13fa..6736c1502 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -209,7 +209,8 @@ $(TR $(TD Objects) $(TD $(REG_START Character classes ) $(REG_TABLE $(REG_TITLE Pattern element, Semantics ) - $(REG_ROW Any atom, Has the same meaning as outside of a character class.) + $(REG_ROW Any atom, Has the same meaning as outside of a character class. + Except for ] which has to be written as \[) $(REG_ROW a-z, Includes characters a, b, c, ..., z. ) $(REG_ROW [a||b]$(COMMA) [a--b]$(COMMA) [a~~b]$(COMMA) [a$(AMP)$(AMP)b], Where a, b are arbitrary classes, means union, set difference, From b5438373ca6ad757bcc7fdd4d40cd562104b1a51 Mon Sep 17 00:00:00 2001 From: Panke Date: Sat, 12 Dec 2020 14:01:53 +0100 Subject: [PATCH 20/21] Update package.d --- std/regex/package.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/regex/package.d b/std/regex/package.d index 6736c1502..44abcd1ea 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -210,7 +210,7 @@ $(TR $(TD Objects) $(TD $(REG_TABLE $(REG_TITLE Pattern element, Semantics ) $(REG_ROW Any atom, Has the same meaning as outside of a character class. - Except for ] which has to be written as \[) + Except for ] which has to be written as \]) $(REG_ROW a-z, Includes characters a, b, c, ..., z. ) $(REG_ROW [a||b]$(COMMA) [a--b]$(COMMA) [a~~b]$(COMMA) [a$(AMP)$(AMP)b], Where a, b are arbitrary classes, means union, set difference, From 06dbaa1ed54dc1af5852514ed643b18a17e18adf Mon Sep 17 00:00:00 2001 From: Tobias Pankrath Date: Sat, 12 Dec 2020 14:53:29 +0100 Subject: [PATCH 21/21] review comments --- std/regex/package.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/regex/package.d b/std/regex/package.d index 44abcd1ea..57a6bee7d 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -209,8 +209,8 @@ $(TR $(TD Objects) $(TD $(REG_START Character classes ) $(REG_TABLE $(REG_TITLE Pattern element, Semantics ) - $(REG_ROW Any atom, Has the same meaning as outside of a character class. - Except for ] which has to be written as \]) + $(REG_ROW Any atom, Has the same meaning as outside of a character class, + except for ] which must be written as \\]) $(REG_ROW a-z, Includes characters a, b, c, ..., z. ) $(REG_ROW [a||b]$(COMMA) [a--b]$(COMMA) [a~~b]$(COMMA) [a$(AMP)$(AMP)b], Where a, b are arbitrary classes, means union, set difference,