From 72f395084373b8c15518def33216485301e8de8a Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Fri, 30 Sep 2016 22:26:40 -0400 Subject: [PATCH] Fix Issue 16566 - hasLength should enforce that length has type size_t --- changelog/std-range-has-length.dd | 8 ++++ std/range/primitives.d | 66 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 changelog/std-range-has-length.dd diff --git a/changelog/std-range-has-length.dd b/changelog/std-range-has-length.dd new file mode 100644 index 00000000000..eaa97a6734d --- /dev/null +++ b/changelog/std-range-has-length.dd @@ -0,0 +1,8 @@ +`hasLength` now enforces that `length` has type `size_t` + +Historically `hasLength!R` yielded `true` for types whereby +`R.length` returns other types convertible to `ulong`, such as `int`, `ushort`, +`const(size_t)`, user-defined types using `alias this`, or notably `ulong` on +32-bit systems. This behavior has been deprecated. After December 2017, +$(REF hasLength, std, range, primitives) will yield `true` only if `R.length` +yields the exact type `size_t`. diff --git a/std/range/primitives.d b/std/range/primitives.d index aeedf605601..25a35dafdf8 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1390,28 +1390,56 @@ template hasLvalueElements(R) } /** -Returns $(D true) if $(D R) has a $(D length) member that returns an -integral type. $(D R) does not have to be a range. Note that $(D -length) is an optional primitive as no range must implement it. Some -ranges do not store their length explicitly, some cannot compute it -without actually exhausting the range (e.g. socket streams), and some -other ranges may be infinite. - -Although narrow string types ($(D char[]), $(D wchar[]), and their -qualified derivatives) do define a $(D length) property, $(D -hasLength) yields $(D false) for them. This is because a narrow -string's length does not reflect the number of characters, but instead -the number of encoding units, and as such is not useful with -range-oriented algorithms. - */ +Yields `true` if `R` has a `length` member that returns a value of `size_t` +type. `R` does not have to be a range. If `R` is a range, algorithms in the +standard library are only guaranteed to support `length` with type `size_t`. + +Note that `length` is an optional primitive as no range must implement it. Some +ranges do not store their length explicitly, some cannot compute it without +actually exhausting the range (e.g. socket streams), and some other ranges may +be infinite. + +Although narrow string types (`char[]`, `wchar[]`, and their qualified +derivatives) do define a `length` property, `hasLength` yields `false` for them. +This is because a narrow string's length does not reflect the number of +characters, but instead the number of encoding units, and as such is not useful +with range-oriented algorithms. To use strings as random-access ranges with +length, use $(REF representation, std, string) or $(REF byCodeUnit, std, utf). + +Deprecation: Historically `hasLength!R` yielded `true` for types whereby +`R.length` returns other types convertible to `ulong`, such as `int`, `ushort`, +`const(size_t)`, user-defined types using `alias this`, or notably `ulong` on +32-bit systems. This behavior has been deprecated. After December 2017, +`hasLength` will yield `true` only if `R.length` yields the exact type `size_t`. +*/ template hasLength(R) { - enum bool hasLength = !isNarrowString!R && is(typeof( - (inout int = 0) + static if (is(typeof(((R* r) => r.length)(null)) Length)) { - R r = R.init; - ulong l = r.length; - })); + static if (is(Length == size_t)) + { + enum bool hasLength = !isNarrowString!R; + } + else static if (is(Length : ulong)) + { + // @@@DEPRECATED_2017-12@@@ + // Uncomment the deprecated(...) message and take the pragma(msg) + // out once https://issues.dlang.org/show_bug.cgi?id=10181 is fixed. + pragma(msg, __FILE__ ~ "(" ~ __LINE__.stringof ~ + "): Note: length must have type size_t on all systems" ~ + ", please update your code by December 2017."); + //deprecated("length must have type size_t on all systems") + enum bool hasLength = true; + } + else + { + enum bool hasLength = false; + } + } + else + { + enum bool hasLength = false; + } } ///