From 27cfc6055cf2ded5e2c9f854692d153ebed1687f Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 2 Mar 2024 20:16:13 -0700 Subject: [PATCH 01/57] Add the beginnings of lib.sys.traits. (#8930) This starts lib.sys.traits with just Unconst, Unshared, and Unqual. Both the tests and documentation have been beefed up from what std.traits has, with many more examples provided to try to make the exact semantics crystal clear, whereas the previous explanation in the docs was pretty sparse. The actual semantics are the same though. This isn't what I would have chosen for traits first, but they're used heavily in unit tests, so they got moved up in the list. I used version (StdDdoc) for the ddoc version, since that's what we do normally in Phobos. I don't know if we'll want or need to change that later given than Phobos v3 technically isn't Std, but all that really matters there is that it's a specific version identifier that we use for building Phobos' documentation and which no other projects would use. And since Phobos v3 currently is set up to build with dub rather than Phobos' normal build system, I expect that the documentation generation is completely busted right now anyway. So, that'll need to get fixed at some point. --- lib/sys/traits.d | 330 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 lib/sys/traits.d diff --git a/lib/sys/traits.d b/lib/sys/traits.d new file mode 100644 index 00000000000..b84088f2e98 --- /dev/null +++ b/lib/sys/traits.d @@ -0,0 +1,330 @@ +// Written in the D programming language +/++ + Templates which extract information about types and symbols at compile time. + + In the context of lib.sys.traits, a "trait" is a template which evaluates to + $(D true) or $(D false), telling the code using it whether the given + arguments match / have that specific trait (e.g. whether the given type is + a dynamic array or whether the given function is $(D @safe)). These traits + are then used primarily in template constraints so that they can test that + the template arguments meet the criteria required by those templates, though + they can be useful in a variety of compile-time contexts + (e.g. the condition of a $(D static if)). + + So, the symbols provided in this module are largely either traits or + templates designed to be used with traits (e.g. $(LREF Unconst) can be used + in a template constraint to get the mutable version of that type so that + the traits used then test the type without worrying about constness). + + $(SCRIPT inhibitQuickIndex = 1;) + + $(BOOKTABLE , + $(TR $(TH Category) $(TH Templates)) + $(TR $(TD Traits for removing type qualfiers) $(TD + $(LREF Unconst) + $(LREF Unshared) + $(LREF Unqual) + )) + ) + + Copyright: Copyright The D Language Foundation 2005 - 2024. + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) + $(HTTP digitalmars.com, Walter Bright), + Tomasz Stachowiak (`isExpressions`), + $(HTTP erdani.org, Andrei Alexandrescu), + Shin Fujishiro, + $(HTTP octarineparrot.com, Robert Clipsham), + $(HTTP klickverbot.at, David Nadlinger), + Kenji Hara, + Shoichi Kato + Source: $(PHOBOSSRC lib/sys/traits) ++/ +module lib.sys.traits; + +/++ + Removes the outer layer of $(D const), $(D inout), or $(D immutable) + from type $(D T). + + If none of those qualifiers have been applied to the outer layer of + type $(D T), then the result is $(D T). + + Due to limitations with D's type system, user-defined types have the type + qualifier removed entirely if present. The types of the member variables + themselves are unaffected beyond how removing the type qualifier from the + type containing them affects them (e.g. an $(D int*) member that is + $(D const(int*)) because the type containing it is $(D const) becomes + $(D int*) when Unconst is used on the containing type, because $(D const) + is removed from the containing type. The member does not become + $(D const(int)*) as would occur if Unconst were used directly on a + $(D const(int*))). + + Also, Unconst has no effect on what a templated type is instantiated with, + so if a templated type is instantiated with a template argument which is a + const type, the template instantiation will not change. + +/ +version (StdDdoc) template Unconst(T) +{ + import core.internal.traits : CoreUnconst = Unconst; + alias Unconst = CoreUnconst!T; +} +else +{ + import core.internal.traits : CoreUnconst = Unconst; + alias Unconst = CoreUnconst; +} + +/// +@safe unittest +{ + static assert(is(Unconst!( int) == int)); + static assert(is(Unconst!( const int) == int)); + static assert(is(Unconst!( inout int) == int)); + static assert(is(Unconst!( inout const int) == int)); + static assert(is(Unconst!(shared int) == shared int)); + static assert(is(Unconst!(shared const int) == shared int)); + static assert(is(Unconst!(shared inout int) == shared int)); + static assert(is(Unconst!(shared inout const int) == shared int)); + static assert(is(Unconst!( immutable int) == int)); + + // Only the outer layer of immutable is removed. + // immutable(int[]) -> immutable(int)[] + alias ImmIntArr = immutable(int[]); + static assert(is(Unconst!ImmIntArr == immutable(int)[])); + + // Only the outer layer of const is removed. + // immutable(int*) -> immutable(int)* + alias ConstIntPtr = const(int*); + static assert(is(Unconst!ConstIntPtr == const(int)*)); + + // const(int)* -> const(int)* + alias PtrToConstInt = const(int)*; + static assert(is(Unconst!PtrToConstInt == const(int)*)); + + static struct S + { + int* ptr; + } + + const S s; + static assert(is(typeof(s) == const S)); + static assert(is(typeof(typeof(s).ptr) == const int*)); + + // For user-defined types, the const qualifier is removed entirely. + // const S -> S + static assert(is(Unconst!(typeof(s)) == S)); + static assert(is(typeof(Unconst!(typeof(s)).ptr) == int*)); + + static struct Foo(T) + { + T* ptr; + } + + // The qualifer on the type is removed, but the qualifier on the template + // argument is not. + static assert(is(Unconst!(const(Foo!(const int))) == Foo!(const int))); + static assert(is(Unconst!(Foo!(const int)) == Foo!(const int))); + static assert(is(Unconst!(const(Foo!int)) == Foo!int)); +} + +/++ + Removes the outer layer of $(D shared) from type $(D T). + + If $(D shared) has not been applied to the outer layer of type $(D T), then + the result is $(D T). + + Note that while $(D immutable) is implicitly $(D shared), it is unaffected + by Unshared. Only explict $(D shared) is removed. + + Due to limitations with D's type system, user-defined types have the type + qualifier removed entirely if present. The types of the member variables + themselves are unaffected beyond how removing the type qualifier from the + type containing them affects them (e.g. an $(D int*) member that is + $(D shared(int*)) because the type containing it is $(D shared) becomes + $(D int*) when Unshared is used on the containing type, because $(D shared) + is removed from the containing type. The member does not become + $(D shared(int)*) as would occur if Unshared were used directly on a + $(D shared(int*))). + + Also, Unshared has no effect on what a templated type is instantiated with, + so if a templated type is instantiated with a template argument which is a + shared type, the template instantiation will not change. + +/ +template Unshared(T) +{ + static if (is(T == shared U, U)) + alias Unshared = U; + else + alias Unshared = T; +} + +/// +@safe unittest +{ + static assert(is(Unshared!( int) == int)); + static assert(is(Unshared!( const int) == const int)); + static assert(is(Unshared!( inout int) == inout int)); + static assert(is(Unshared!( inout const int) == inout const int)); + static assert(is(Unshared!(shared int) == int)); + static assert(is(Unshared!(shared const int) == const int)); + static assert(is(Unshared!(shared inout int) == inout int)); + static assert(is(Unshared!(shared inout const int) == inout const int)); + static assert(is(Unshared!( immutable int) == immutable int)); + + // Only the outer layer of shared is removed. + // shared(int[]) -> shared(int)[] + alias SharedIntArr = shared(int[]); + static assert(is(Unshared!SharedIntArr == shared(int)[])); + + // Only the outer layer of shared is removed. + // shared(int*) -> shared(int)* + alias SharedIntPtr = shared(int*); + static assert(is(Unshared!SharedIntPtr == shared(int)*)); + + // shared(int)* -> shared(int)* + alias PtrToSharedInt = shared(int)*; + static assert(is(Unshared!PtrToSharedInt == shared(int)*)); + + // immutable is unaffected + alias ImmutableArr = immutable(int[]); + static assert(is(Unshared!ImmutableArr == immutable(int[]))); + + static struct S + { + int* ptr; + } + + shared S s; + static assert(is(typeof(s) == shared S)); + static assert(is(typeof(typeof(s).ptr) == shared int*)); + + // For user-defined types, the shared qualifier is removed entirely. + // shared S -> S + static assert(is(Unshared!(typeof(s)) == S)); + static assert(is(typeof(Unshared!(typeof(s)).ptr) == int*)); + + static struct Foo(T) + { + T* ptr; + } + + // The qualifer on the type is affected, but the qualifier on the template + // argument is not. + static assert(is(Unshared!(shared(Foo!(shared int))) == Foo!(shared int))); + static assert(is(Unshared!(Foo!(shared int)) == Foo!(shared int))); + static assert(is(Unshared!(shared(Foo!int)) == Foo!int)); +} + +/++ + Removes the outer layer of all type qualifiers from type $(D T). + + If no type qualifiers have been applied to the outer layer of type $(D T), + then the result is $(D T). + + Due to limitations with D's type system, user-defined types have the type + qualifier removed entirely if present. The types of the member variables + themselves are unaffected beyond how removing the type qualifier from the + type containing them affects them (e.g. a $(D int*) member that is + $(D const(int*)) because the type containing it is $(D const) becomes + $(D int*) when Unqual is used on the containing type, because $(D const) + is removed from the containing type. The member does not become + $(D const(int)*) as would occur if Unqual were used directly on a + $(D const(int*))). + + Also, Unqual has no effect on what a templated type is instantiated with, + so if a templated type is instantiated with a template argument which has a + type qualifier, the template instantiation will not change. + + Note that in most cases, $(LREF Unconst) or $(LREF Unshared) should be used + rather than Unqual, because in most cases, code is not designed to work with + $(D shared) and thus doing type checks which remove $(D shared) will allow + $(D shared) types to pass template constraints when they won't actually + work with the code. And when code is designed to work with $(D shared), + it's often the case that the type checks need to take $(D const) into + account to work properly. + + In particular, historically, a lot of D code has used Unqual when the + programmer's intent was to remove $(D const), and $(D shared) wasn't + actually considered at all. And in such cases, the code really should use + $(LREF Unconst) instead. + + But of course, if a template constraint or $(D static if) really needs to + strip off both the mutability qualifers and $(D shared) for what it's + testing for, then that's what Unqual is for. + +/ +version (StdDdoc) template Unqual(T) +{ + import core.internal.traits : CoreUnqual = Unqual; + alias Unqual = CoreUnqual!(T); +} +else +{ + import core.internal.traits : CoreUnqual = Unqual; + alias Unqual = CoreUnqual; +} + +@safe unittest +{ + static assert(is(Unqual!( int) == int)); + static assert(is(Unqual!( const int) == int)); + static assert(is(Unqual!( inout int) == int)); + static assert(is(Unqual!( inout const int) == int)); + static assert(is(Unqual!(shared int) == int)); + static assert(is(Unqual!(shared const int) == int)); + static assert(is(Unqual!(shared inout int) == int)); + static assert(is(Unqual!(shared inout const int) == int)); + static assert(is(Unqual!( immutable int) == int)); + + // Only the outer layer of immutable is removed. + // immutable(int[]) -> immutable(int)[] + alias ImmIntArr = immutable(int[]); + static assert(is(Unqual!ImmIntArr == immutable(int)[])); + + // Only the outer layer of const is removed. + // const(int*) -> const(int)* + alias ConstIntPtr = const(int*); + static assert(is(Unqual!ConstIntPtr == const(int)*)); + + // const(int)* -> const(int)* + alias PtrToConstInt = const(int)*; + static assert(is(Unqual!PtrToConstInt == const(int)*)); + + // Only the outer layer of shared is removed. + // shared(int*) -> shared(int)* + alias SharedIntPtr = shared(int*); + static assert(is(Unqual!SharedIntPtr == shared(int)*)); + + // shared(int)* -> shared(int)* + alias PtrToSharedInt = shared(int)*; + static assert(is(Unqual!PtrToSharedInt == shared(int)*)); + + // Both const and shared are removed from the outer layer. + // shared const int[] -> shared(const(int))[] + alias SharedConstIntArr = shared const(int[]); + static assert(is(Unqual!SharedConstIntArr == shared(const(int))[])); + + static struct S + { + int* ptr; + } + + shared const S s; + static assert(is(typeof(s) == shared const S)); + static assert(is(typeof(typeof(s).ptr) == shared const int*)); + + // For user-defined types, the qualifiers are removed entirely. + // shared const S -> S + static assert(is(Unqual!(typeof(s)) == S)); + static assert(is(typeof(Unqual!(typeof(s)).ptr) == int*)); + + static struct Foo(T) + { + T* ptr; + } + + // The qualifers on the type are affected, but the qualifiers on the + // template argument is not. + static assert(is(Unqual!(const(Foo!(const int))) == Foo!(const int))); + static assert(is(Unqual!(Foo!(const int)) == Foo!(const int))); + static assert(is(Unqual!(const(Foo!int)) == Foo!int)); +} From bfe2a183893df370e270149e2c886b050517250a Mon Sep 17 00:00:00 2001 From: Paul Backus Date: Sat, 2 Mar 2024 20:12:40 -0500 Subject: [PATCH 02/57] sumtype: add hint to "never matches" message A common cause of the "handler never matches" error is a template handler that contains a typo or other compile-time error. Since the location of the actual error is suppressed by __traits(compiles), users often do not think to look for a mistake inside the handler itself. Now, the message itself includes a hint to do so. --- std/sumtype.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/sumtype.d b/std/sumtype.d index 4e76156b1cb..80ce73d6a75 100644 --- a/std/sumtype.d +++ b/std/sumtype.d @@ -1988,7 +1988,7 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) ? "template" : typeof(handler).stringof ) ~ "` " ~ - "never matches" + "never matches. Perhaps the handler failed to compile" ); } From 837b7deb7d6edab28fb93d452201d27e2c0a4bc5 Mon Sep 17 00:00:00 2001 From: Paul Backus Date: Sun, 3 Mar 2024 17:56:32 -0500 Subject: [PATCH 03/57] std.range.choose: call payload postblit correctly Fixes bugzilla issue 15708 --- std/range/package.d | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/std/range/package.d b/std/range/package.d index 30f6ffb9f44..d9d74fb21a9 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -1882,7 +1882,7 @@ private struct ChooseResult(Ranges...) this(this) { actOnChosen!((ref r) { - static if (hasElaborateCopyConstructor!(typeof(r))) r.__postblit(); + static if (hasElaborateCopyConstructor!(typeof(r))) r.__xpostblit(); })(this); } @@ -2177,6 +2177,29 @@ pure @safe nothrow unittest assert(chosen2.front.v == 4); } +// https://issues.dlang.org/show_bug.cgi?id=15708 +@safe unittest +{ + static struct HasPostblit + { + this(this) {} + } + + static struct Range + { + bool empty; + int front; + void popFront() {} + HasPostblit member; + } + + Range range; + int[] arr; + + auto chosen = choose(true, range, arr); + auto copy = chosen; +} + /** Choose one of multiple ranges at runtime. From 04b1563ef124ddff6e77e680284c0fd8c8a21b24 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 4 Mar 2024 07:29:43 +0000 Subject: [PATCH 04/57] std.path: Note that absolutePath and co. do not collapse ".." --- std/path.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/std/path.d b/std/path.d index 449235a6820..2dbdf418f33 100644 --- a/std/path.d +++ b/std/path.d @@ -2732,6 +2732,9 @@ else version (Posix) The function allocates memory if and only if it gets to the third stage of this algorithm. + Note that `absolutePath` will not normalize `..` segments. + Use `buildNormalizedPath(absolutePath(path))` if that is desired. + Params: path = the relative path to transform base = the base directory of the relative path @@ -2815,6 +2818,9 @@ string absolutePath(return scope const string path, lazy string base = getcwd()) which allocates memory.) ) + Note that `asAbsolutePath` will not normalize `..` segments. + Use `asNormalizedPath(asAbsolutePath(path))` if that is desired. + Params: path = the relative path to transform From e7ddb170ab04ab9c1637b92854b6518222843064 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Tue, 5 Mar 2024 01:25:03 -0700 Subject: [PATCH 05/57] Add Map to lib.sys.meta. (#8934) This ports std.meta.staticMap to lib.sys.meta as Map. The implementation is the same, but the documentation and tests have been expanded. As has been discussed before, the way that std.meta names things is inconsistent, and the pattern that lib.sys.meta is going with is that its templates will be CamelCased like types, which matches what some of std.meta does but not all of it. --- lib/sys/meta.d | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/sys/meta.d b/lib/sys/meta.d index 0f359ac5750..e31dbb03be9 100644 --- a/lib/sys/meta.d +++ b/lib/sys/meta.d @@ -20,6 +20,9 @@ $(LREF Alias) $(LREF AliasSeq) )) + $(TR $(TD Alias sequence transformation) $(TD + $(LREF Map) + )) ) References: @@ -147,3 +150,64 @@ alias Alias(T) = T; assert(i == 7); } } + +/++ + Map takes a template predicate and applies it to every element in the given + $(D AliasSeq), resulting in an $(D AliasSeq) with the transformed elements. + + So, it's equivalent to + `AliasSeq!(fun!(args[0]), fun!(args[1]), ..., fun!(args[$ - 1]))`. + +/ +template Map(alias fun, args...) +{ + alias Map = AliasSeq!(); + static foreach (arg; args) + Map = AliasSeq!(Map, fun!arg); +} + +/// +@safe unittest +{ + import lib.sys.traits : Unqual; + + // empty + alias Empty = Map!Unqual; + static assert(Empty.length == 0); + + // single + alias Single = Map!(Unqual, const int); + static assert(is(Single == AliasSeq!int)); + + // several + alias Several = Map!(Unqual, int, const int, immutable int, uint, + ubyte, byte, short, ushort, const long); + static assert(is(Several == AliasSeq!(int, int, int, uint, + ubyte, byte, short, ushort, long))); + + alias ToDynamicArray(T) = T[]; + + alias Arrays = Map!(ToDynamicArray, int, const ubyte, string); + static assert(is(Arrays == AliasSeq!(int[], const(ubyte)[], string[]))); +} + +// @@@ BUG @@@ The test below exposes failure of the straightforward use. +// See @adamdruppe's comment to https://github.com/dlang/phobos/pull/8039 +@safe unittest +{ + template id(alias what) + { + enum id = __traits(identifier, what); + } + + enum A { a } + static assert(Map!(id, A.a) == AliasSeq!"a"); +} + +// regression test for https://issues.dlang.org/show_bug.cgi?id=21088 +@system unittest // typeid opEquals is @system +{ + enum getTypeId(T) = typeid(T); + alias A = Map!(getTypeId, int); + + assert(A == typeid(int)); +} From 7bc7d988b1bb36b273d70a606c52596a5f2756ff Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Tue, 5 Mar 2024 02:26:39 -0700 Subject: [PATCH 06/57] Some documentation improvements to lib.sys.traits and lib.sys.meta. (#8937) --- lib/sys/meta.d | 51 ++++++++++++++++++++++++++++++++++++++++++++++-- lib/sys/traits.d | 20 +++++++++---------- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/lib/sys/meta.d b/lib/sys/meta.d index e31dbb03be9..29543c29178 100644 --- a/lib/sys/meta.d +++ b/lib/sys/meta.d @@ -12,6 +12,39 @@ For more information, see $(DDLINK ctarguments, Compile-time Sequences, Compile-time Sequences). + One thing that should be noted is that while the templates provided in this + module can be extremely useful, they generally should not be used with lists + of values. The language uses alias sequences for a variety of things + (including both parameter lists and argument lists), so they can contain + types, symbols, values, or a mixture of them all. The ability to manipulate + types and symbols within alias sequences is vital, because that's really + the only way to do it. However, because D has CTFE (Compile-Time Function + Evaluation), making it possible to call many functions at compile time, if + code needs to be able to manipulate values at compile-time, CTFE is + typically much more efficient and easier to do. Instantiating a bunch of + templates to manipulate values is incredibly inefficient in comparison. + + So, while many of the templates in this module will work with values simply + because alias sequences can contain values, most code should restrict + itself to using them for operating on types or symbols - i.e. the stuff + where CTFE can't be used. That being said, there will be times when one can + be used to feed into the other. E.G. + --- + alias Types = AliasSeq!(int, byte, ulong, int[10]); + + enum Sizeof(T) = T.sizeof; + + alias sizesAsAliasSeq = Map!(Sizeof, Types); + static assert(sizesAsAliasSeq == AliasSeq!(4, 1, 8, 40)); + + enum size_t[] sizes = [sizesAsAliasSeq]; + static assert(sizes == [4, 1, 8, 40]); + --- + + Just be aware that if CTFE can be used for a particular task, it's better to + use CTFE than to manipulate alias sequences with the kind of templates + provided by this module. + $(SCRIPT inhibitQuickIndex = 1;) $(DIVC quickindex, $(BOOKTABLE , @@ -40,6 +73,20 @@ +/ module lib.sys.meta; +// Example for converting types to values from module documentation. +@safe unittest +{ + alias Types = AliasSeq!(int, byte, ulong, int[10]); + + enum Sizeof(T) = T.sizeof; + + alias sizesAsAliasSeq = Map!(Sizeof, Types); + static assert(sizesAsAliasSeq == AliasSeq!(4, 1, 8, 40)); + + enum size_t[] sizes = [sizesAsAliasSeq]; + static assert(sizes == [4, 1, 8, 40]); +} + /++ Creates a sequence of zero or more aliases. This is most commonly used as template parameters or arguments. @@ -103,7 +150,7 @@ alias Alias(T) = T; @safe unittest { // Without Alias this would fail if Args[0] were e.g. a value and - // some logic would be needed to detect when to use enum instead + // some logic would be needed to detect when to use enum instead. alias Head(Args...) = Alias!(Args[0]); alias Tail(Args...) = Args[1 .. $]; @@ -140,7 +187,7 @@ alias Alias(T) = T; } { alias A = Alias!(AliasSeq!int); - static assert(!is(typeof(A[0]))); //not an AliasSeq + static assert(!is(typeof(A[0]))); // An Alias is not an AliasSeq. static assert(is(A == int)); } { diff --git a/lib/sys/traits.d b/lib/sys/traits.d index b84088f2e98..53be37369b6 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -2,20 +2,20 @@ /++ Templates which extract information about types and symbols at compile time. - In the context of lib.sys.traits, a "trait" is a template which evaluates to + In the context of lib.sys.traits, a "trait" is a template which provides + information about a type or symbol. Most traits evaluate to $(D true) or $(D false), telling the code using it whether the given arguments match / have that specific trait (e.g. whether the given type is - a dynamic array or whether the given function is $(D @safe)). These traits - are then used primarily in template constraints so that they can test that - the template arguments meet the criteria required by those templates, though - they can be useful in a variety of compile-time contexts + a dynamic array or whether the given function is $(D @safe)). However, some + traits may provide other kinds of information about a type (e.g. the trait + could evaluate to the base type for an enum type, or it could strip + $(D const) from the type to provide the mutable version of that type). + + These traits are then used primarily in template constraints so that they + can test that the template arguments meet the criteria required by those + templates, though they can be useful in a variety of compile-time contexts (e.g. the condition of a $(D static if)). - So, the symbols provided in this module are largely either traits or - templates designed to be used with traits (e.g. $(LREF Unconst) can be used - in a template constraint to get the mutable version of that type so that - the traits used then test the type without worrying about constness). - $(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , From 3579225931ad9b0d8855b5be1e61c690350b367a Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Tue, 5 Mar 2024 02:30:45 -0700 Subject: [PATCH 07/57] Add ConstOf, ImmutableOf, InoutOf, and SharedOf to lib.sys.traits. These do the same as their std.traits counterparts, but I've beefed up the documentation and examples a bit. I'm not sure that it's worth adding SharedConstOf, SharedInoutOf, or SharedConstInoutOf as well, since that seems kind of excessive to me - though it looks like std.variant does use SharedConstOf in its tests - but in either case, I think that those can be left for a later commit if we want to keep them, since this is already adding four symbols. --- lib/sys/traits.d | 167 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/lib/sys/traits.d b/lib/sys/traits.d index 53be37369b6..7a77d9a5c11 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -25,6 +25,12 @@ $(LREF Unshared) $(LREF Unqual) )) + $(TR $(TD Type Constructors) $(TD + $(LREF ConstOf) + $(LREF ImmutableOf) + $(LREF InoutOf) + $(LREF SharedOf) + )) ) Copyright: Copyright The D Language Foundation 2005 - 2024. @@ -328,3 +334,164 @@ else static assert(is(Unqual!(Foo!(const int)) == Foo!(const int))); static assert(is(Unqual!(const(Foo!int)) == Foo!int)); } + +/++ + Applies $(D const) to the given type. + + This is primarily useful in conjunction with templates that take a template + predicate (such as many of the templates in lib.sys.meta), since while in + most cases, you can simply do $(D const T) or $(D const(T)) to make $(D T) + $(D const), with something like $(REF Map, lib, sys, meta), you need to + pass a template to be applied. + + See_Also: + $(LREF ImmutableOf) + $(LREF InoutOf) + $(LREF SharedOf) + +/ +alias ConstOf(T) = const T; + +/// +@safe unittest +{ + static assert(is(ConstOf!int == const int)); + static assert(is(ConstOf!(const int) == const int)); + static assert(is(ConstOf!(inout int) == inout const int)); + static assert(is(ConstOf!(shared int) == const shared int)); + + // Note that const has no effect on immutable. + static assert(is(ConstOf!(immutable int) == immutable int)); + + import lib.sys.meta : AliasSeq, Map; + + alias Types = AliasSeq!(int, long, + bool*, ubyte[], + string, immutable(string)); + alias WithConst = Map!(ConstOf, Types); + static assert(is(WithConst == + AliasSeq!(const int, const long, + const(bool*), const(ubyte[]), + const(string), immutable(string)))); +} + +/++ + Applies $(D immutable) to the given type. + + This is primarily useful in conjunction with templates that take a template + predicate (such as many of the templates in lib.sys.meta), since while in + most cases, you can simply do $(D immutable T) or $(D immutable(T)) to make + $(D T) $(D immutable), with something like $(REF Map, lib, sys, meta), you + need to pass a template to be applied. + + See_Also: + $(LREF ConstOf) + $(LREF InoutOf) + $(LREF SharedOf) + +/ +alias ImmutableOf(T) = immutable T; + +/// +@safe unittest +{ + static assert(is(ImmutableOf!int == immutable int)); + + // Note that immutable overrides const and inout. + static assert(is(ImmutableOf!(const int) == immutable int)); + static assert(is(ImmutableOf!(inout int) == immutable int)); + + // Note that immutable overrides shared, since immutable is implicitly + // shared. + static assert(is(ImmutableOf!(shared int) == immutable int)); + + static assert(is(ImmutableOf!(immutable int) == immutable int)); + + import lib.sys.meta : AliasSeq, Map; + + alias Types = AliasSeq!(int, long, + bool*, ubyte[], + string, immutable(string)); + alias WithImmutable = Map!(ImmutableOf, Types); + static assert(is(WithImmutable == + AliasSeq!(immutable int, immutable long, + immutable(bool*), immutable(ubyte[]), + immutable(string), immutable(string)))); +} + +/++ + Applies $(D inout) to the given type. + + This is primarily useful in conjunction with templates that take a template + predicate (such as many of the templates in lib.sys.meta), since while in + most cases, you can simply do $(D inout T) or $(D inout(T)) to make $(D T) + $(D inout), with something like $(REF Map, lib, sys, meta), you need to + pass a template to be applied. + + See_Also: + $(LREF ConstOf) + $(LREF ImmutableOf) + $(LREF SharedOf) + +/ +alias InoutOf(T) = inout T; + +/// +@safe unittest +{ + static assert(is(InoutOf!int == inout int)); + static assert(is(InoutOf!(const int) == inout const int)); + static assert(is(InoutOf!(inout int) == inout int)); + static assert(is(InoutOf!(shared int) == inout shared int)); + + // Note that inout has no effect on immutable. + static assert(is(InoutOf!(immutable int) == immutable int)); + + import lib.sys.meta : AliasSeq, Map; + + alias Types = AliasSeq!(int, long, + bool*, ubyte[], + string, immutable(string)); + alias WithInout = Map!(InoutOf, Types); + static assert(is(WithInout == + AliasSeq!(inout int, inout long, + inout(bool*), inout(ubyte[]), + inout(string), immutable(string)))); +} + +/++ + Applies $(D shared) to the given type. + + This is primarily useful in conjunction with templates that take a template + predicate (such as many of the templates in lib.sys.meta), since while in + most cases, you can simply do $(D shared T) or $(D shared(T)) to make $(D T) + $(D shared), with something like $(REF Map, lib, sys, meta), you need to + pass a template to be applied. + + See_Also: + $(LREF ConstOf) + $(LREF ImmutableOf) + $(LREF InoutOf) + +/ +alias SharedOf(T) = shared T; + +/// +@safe unittest +{ + static assert(is(SharedOf!int == shared int)); + static assert(is(SharedOf!(const int) == const shared int)); + static assert(is(SharedOf!(inout int) == inout shared int)); + static assert(is(SharedOf!(shared int) == shared int)); + + // Note that shared has no effect on immutable, since immutable is + // implicitly shared. + static assert(is(SharedOf!(immutable int) == immutable int)); + + import lib.sys.meta : AliasSeq, Map; + + alias Types = AliasSeq!(int, long, + bool*, ubyte[], + string, immutable(string)); + alias WithShared = Map!(SharedOf, Types); + static assert(is(WithShared == + AliasSeq!(shared int, shared long, + shared(bool*), shared(ubyte[]), + shared(string), immutable(string)))); +} From 69a5122fad0bb25fda918316b2c8ded29c1aa63d Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 7 Mar 2024 00:48:31 -0700 Subject: [PATCH 08/57] Add isDynamicArray and isStaticArray to lib.sys.traits. (#8939) Their behavior does differ slightly from that of their std.traits counterparts, because they are false for enums even when an enum's base type would match. std.traits is inconsistent with how it treats enums (e.g. isSomeString is false for them, because it was causing bugs with ranges for them to pass, but many of the other traits are true for enums), but unfortunately, this isn't something that we've been able to fix in std.traits (std.traits' isDynamicArray even mentions the problem in a comment in its documentation), since it would break exising code (in potentially very subtle ways no less). I added a section to the module documentation explaining the reasoning for how lib.sys.traits handles enums and implicit conversions with the isXXX and hasXXX traits, which will hopefully not only make the situation clear but also help educate some folks on the issues with implicit conversions and template constraints. I also beefed up the tests and documentation for isDynamicArray and isStaticArray. --- lib/sys/traits.d | 243 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/lib/sys/traits.d b/lib/sys/traits.d index 7a77d9a5c11..9340e5fec55 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -16,6 +16,36 @@ templates, though they can be useful in a variety of compile-time contexts (e.g. the condition of a $(D static if)). + Note that unless otherwise specified, the isXXXX and hasXXX traits in this + module are checking for exact matches, so base types (e.g. with enums) and + other implicit conversions do not factor into whether such traits are true + or false. The type itself is being checked, not what it can be converted + to. + + This is because these traits are often used in templated constraints, and + having a type pass a template constraint based on an implicit conversion + but then not have the implicit conversion actually take place (which it + won't unless the template does something to force it internally) can lead + to either compilation errors or subtle behavioral differences - and even + when the conversion is done explicitly within a templated function, since + it's not done at the call site, it can still lead to subtle bugs in some + cases (e.g. if slicing a static array is involved). + + So, it's typically best to be explicit and clear about a template constraint + accepting any kind of implicit conversion rather than having it buried in a + trait where programmers stand a good chance of using the trait without + realizing that enums might pass based on their base type - or that a type + might pass based on some other implicit conversion. + + Regardless of what a trait is testing for, the documentation strives to be + $(I very) clear about what the trait does, and of course, the names do try + to make it clear as well - though obviously, only so much information can + be put into a name, and some folks will misintrepret some symbols no matter + how well they're named. So, please be sure that you clearly understand what + these traits do when using them, since messing up template constraints can + unfortunately be a great way to introduce subtle bugs into your program. + Either way, of course, unit tests are your friends. + $(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , @@ -48,6 +78,219 @@ +/ module lib.sys.traits; +/++ + Whether type $(D T) is a dynamic array. + + Note that this does not include implicit conversions or enum types. The + type itself must be a dynamic array. + +/ +enum isDynamicArray(T) = is(T == U[], U); + +/// +@safe unittest +{ + static assert( isDynamicArray!(int[])); + static assert( isDynamicArray!(const int[])); + static assert( isDynamicArray!(inout int[])); + static assert( isDynamicArray!(shared(int)[])); + static assert( isDynamicArray!string); + + static struct S + { + int[] arr; + } + + static assert(!isDynamicArray!int); + static assert(!isDynamicArray!(int*)); + static assert(!isDynamicArray!real); + static assert(!isDynamicArray!S); + + // Static arrays. + static assert(!isDynamicArray!(int[5])); + static assert(!isDynamicArray!(const(int)[5])); + + // Dynamic array of static arrays. + static assert( isDynamicArray!(long[3][])); + + // Static array of dynamic arrays. + static assert(!isDynamicArray!(long[][3])); + + // Associative array. + static assert(!isDynamicArray!(int[string])); + + // While typeof(null) gets treated as void[] in some contexts, it is + // distinct from void[] and is not considered to be a dynamic array. + static assert(!isDynamicArray!(typeof(null))); + + // However, naturally, if null is cast to a dynamic array, it's a + // dynamic array, since the cast forces the type. + static assert( isDynamicArray!(typeof(cast(int[]) null))); + + enum E : int[] + { + a = [1, 2, 3], + } + + // Enums do not count. + static assert(!isDynamicArray!E); + + static struct AliasThis + { + int[] arr; + alias this = arr; + } + + // Other implicit conversions do not count. + static assert(!isDynamicArray!AliasThis); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) + { + enum E : Q!T { a = Q!T.init } + + static assert( isDynamicArray!(Q!T)); + static assert(!isDynamicArray!E); + static assert(!isDynamicArray!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(int[51], int[][2], + char[][int][11], immutable char[13u], + const(real)[1], const(real)[1][1], void[0])) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isDynamicArray!(Q!T)); + static assert(!isDynamicArray!E); + static assert(!isDynamicArray!(AliasThis!(Q!T))); + } + } +} + +/++ + Whether type $(D T) is a static array. + + Note that this does not include implicit conversions or enum types. The + type itself must be a static array. This is in contrast to + $(D __traits(isStaticArray, T)) which is true for enums (but not for other + implict conversions to static arrays). + + As explained in the module documentation, traits like this one are not true + for enums (unlike most of the $(D __traits) traits) in order to avoid + testing for implicit conversions by default with template constraints, + since that tends to lead to subtle bugs when the code isn't carefully + written to take implicit conversions into account. + + See also: + $(DDSUBLINK spec/traits, isStaticArray, $(D __traits(isStaticArray, T))) + +/ +enum bool isStaticArray(T) = is(T == U[n], U, size_t n); + +/// +@safe unittest +{ + static assert( isStaticArray!(int[12])); + static assert( isStaticArray!(const int[42])); + static assert( isStaticArray!(inout int[0])); + static assert( isStaticArray!(shared(int)[907])); + static assert( isStaticArray!(immutable(char)[5])); + + static struct S + { + int[4] arr; + } + + static assert(!isStaticArray!int); + static assert(!isStaticArray!(int*)); + static assert(!isStaticArray!real); + static assert(!isStaticArray!S); + + // Dynamic arrays. + static assert(!isStaticArray!(int[])); + static assert(!isStaticArray!(const(int)[])); + static assert(!isStaticArray!string); + + // Static array of dynamic arrays. + static assert( isStaticArray!(long[][3])); + + // Dynamic array of static arrays. + static assert(!isStaticArray!(long[3][])); + + // Associative array. + static assert(!isStaticArray!(int[string])); + + // Of course, null is not considered to be a static array. + static assert(!isStaticArray!(typeof(null))); + + enum E : int[3] + { + a = [1, 2, 3], + } + + // Enums do not count. + static assert(!isStaticArray!E); + + // This is the one place where isStaticArray differs from + // __traits(isStaticArray, ...) + static assert( __traits(isStaticArray, E)); + + static struct AliasThis + { + int[] arr; + alias this = arr; + } + + // Other implicit conversions do not count. + static assert(!isStaticArray!AliasThis); + + static assert(!__traits(isStaticArray, AliasThis)); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(int[51], int[][2], + char[][int][11], immutable char[13u], + const(real)[1], const(real)[1][1], void[0])) + { + enum E : Q!T { a = Q!T.init, } + + static assert( isStaticArray!(Q!T)); + static assert(!isStaticArray!E); + static assert(!isStaticArray!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) + { + enum E : Q!T { a = Q!T.init, } + + static assert(!isStaticArray!(Q!T)); + static assert(!isStaticArray!E); + static assert(!isStaticArray!(AliasThis!(Q!T))); + } + } +} + /++ Removes the outer layer of $(D const), $(D inout), or $(D immutable) from type $(D T). From e16ce73f59ea8fb90b0a15bd54688b59dee9a821 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 8 Mar 2024 00:21:51 -0700 Subject: [PATCH 09/57] Add isSomeString and isSomeChar to lib.sys.traits. (#8940) This ports over isSomeString and isSomeChar from std.traits. isSomeString behaves the same (since the std.traits version doesn't accept enums), but the new isSomeChar is different in that the old one accepts enums, and this one rejects them. The new isSomeChar also has a simplified implementation, but the behavior is the same aside from how enums are treated. The documentation and tests have been beefed up in the process, and I added a bit more to the documentation / tests for isDynamicArray and isStaticArray based on what came up with the tests for isSomeString and isSomeChar. It is not my plan to port over any of the other traits in std.traits which are specifically for strings (e.g. isNarrowString), so checking string types with lib.sys.traits should be less confusing than it has been with std.traits. --- lib/sys/traits.d | 272 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 260 insertions(+), 12 deletions(-) diff --git a/lib/sys/traits.d b/lib/sys/traits.d index 9340e5fec55..a3834231970 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -50,6 +50,13 @@ $(BOOKTABLE , $(TR $(TH Category) $(TH Templates)) + $(TR $(TD Categories of types) $(TD + $(TR $(TD Traits for removing type qualfiers) $(TD + $(LREF isDynamicArray) + $(LREF isSomeChar) + $(LREF isSomeString) + $(LREF isStaticArray) + )) $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) $(LREF Unshared) @@ -89,26 +96,41 @@ enum isDynamicArray(T) = is(T == U[], U); /// @safe unittest { + // Some types which are dynamic arrays. static assert( isDynamicArray!(int[])); static assert( isDynamicArray!(const int[])); static assert( isDynamicArray!(inout int[])); static assert( isDynamicArray!(shared(int)[])); static assert( isDynamicArray!string); - static struct S - { - int[] arr; - } + static assert( isDynamicArray!(typeof([1, 2, 3]))); + static assert( isDynamicArray!(typeof("dlang"))); + + int[] arr; + static assert( isDynamicArray!(typeof(arr))); + // Some types which aren't dynamic arrays. static assert(!isDynamicArray!int); static assert(!isDynamicArray!(int*)); static assert(!isDynamicArray!real); + + static struct S + { + int[] arr; + } static assert(!isDynamicArray!S); + // The struct itself isn't considered a dynamic array, + // but its member variable is when checked directly. + static assert( isDynamicArray!(typeof(S.arr))); + // Static arrays. static assert(!isDynamicArray!(int[5])); static assert(!isDynamicArray!(const(int)[5])); + int[2] sArr = [42, 97]; + static assert(!isDynamicArray!(typeof(sArr))); + // Dynamic array of static arrays. static assert( isDynamicArray!(long[3][])); @@ -165,7 +187,7 @@ enum isDynamicArray(T) = is(T == U[], U); static assert(!isDynamicArray!(AliasThis!(Q!T))); } - foreach (T; AliasSeq!(int[51], int[][2], + foreach (T; AliasSeq!(int, int[51], int[][2], char[][int][11], immutable char[13u], const(real)[1], const(real)[1][1], void[0])) { @@ -200,27 +222,44 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); /// @safe unittest { + // Some types which are static arrays. static assert( isStaticArray!(int[12])); static assert( isStaticArray!(const int[42])); static assert( isStaticArray!(inout int[0])); static assert( isStaticArray!(shared(int)[907])); static assert( isStaticArray!(immutable(char)[5])); - static struct S - { - int[4] arr; - } + // D doesn't have static array literals, but you get the same effect + // by casting a dynamic array literal to a static array, and of course, + // the result is typed as a static array. + static assert( isStaticArray!(typeof(cast(int[3]) [1, 2, 3]))); + int[2] sArr = [1, 2]; + static assert( isStaticArray!(typeof(sArr))); + + // Some types which are not static arrays. static assert(!isStaticArray!int); static assert(!isStaticArray!(int*)); static assert(!isStaticArray!real); + + static struct S + { + int[4] arr; + } static assert(!isStaticArray!S); + // The struct itself isn't considered a static array, + // but its member variable is when checked directly. + static assert( isStaticArray!(typeof(S.arr))); + // Dynamic arrays. static assert(!isStaticArray!(int[])); static assert(!isStaticArray!(const(int)[])); static assert(!isStaticArray!string); + int[] arr; + static assert(!isStaticArray!(typeof(arr))); + // Static array of dynamic arrays. static assert( isStaticArray!(long[][3])); @@ -241,8 +280,7 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); // Enums do not count. static assert(!isStaticArray!E); - // This is the one place where isStaticArray differs from - // __traits(isStaticArray, ...) + // This is where isStaticArray differs from __traits(isStaticArray, ...) static assert( __traits(isStaticArray, E)); static struct AliasThis @@ -280,7 +318,7 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); static assert(!isStaticArray!(AliasThis!(Q!T))); } - foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) + foreach (T; AliasSeq!(int, int[], char[], string, long[3][], double[string][])) { enum E : Q!T { a = Q!T.init, } @@ -291,6 +329,216 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); } } +/++ + Whether the given type is a built-in string type - i.e whether it's a + dynamic array of $(D char), $(D wchar), or $(D dchar), ignoring all + qualifiers. + + Note that this does not include implicit conversions or enum types. The + type itself must be a dynamic array whose element type is one of the three + built-in character types. + +/ +enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar)); + +/// +@safe unittest +{ + // Some types which are string types. + static assert( isSomeString!string); + static assert( isSomeString!wstring); + static assert( isSomeString!dstring); + static assert( isSomeString!(char[])); + static assert( isSomeString!(wchar[])); + static assert( isSomeString!(dchar[])); + static assert( isSomeString!(const char[])); + static assert( isSomeString!(immutable char[])); + static assert( isSomeString!(inout wchar[])); + static assert( isSomeString!(shared wchar[])); + static assert( isSomeString!(const shared dchar[])); + + static assert( isSomeString!(typeof("aaa"))); + static assert( isSomeString!(typeof("aaa"w))); + static assert( isSomeString!(typeof("aaa"d))); + + string s; + static assert( isSomeString!(typeof(s))); + + // Some types which are not strings. + static assert(!isSomeString!int); + static assert(!isSomeString!(int[])); + static assert(!isSomeString!(byte[])); + + // Static arrays of characters are not considered strings. + static assert(!isSomeString!(char[4])); + + static struct S + { + string str; + } + static assert(!isSomeString!S); + + // The struct itself isn't considered a string, + // but its member variable is when checked directly. + static assert( isSomeString!(typeof(S.str))); + + // While strings can be null, typeof(null) is not typed as a string. + static assert(!isSomeString!(typeof(null))); + + // However, naturally, if null is cast to a string type, + // it's a string type, since the cast forces the type. + static assert( isSomeString!(typeof(cast(char[]) null))); + + enum E : string + { + a = "dlang" + } + + // Enums do not count. + static assert(!isSomeString!E); + + static struct AliasThis + { + string str; + alias this = str; + } + + // Other implicit conversions do not count. + static assert(!isSomeString!AliasThis); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(char[], wchar[], dchar[])) + { + enum E : Q!T { a = Q!T.init } + + static assert( isSomeString!(Q!T)); + static assert(!isSomeString!E); + static assert(!isSomeString!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(char, wchar, dchar, int, byte[], ubyte[], int[], char[12], wchar[17], dchar[2], void[])) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isSomeString!(Q!T)); + static assert(!isSomeString!E); + static assert(!isSomeString!(AliasThis!(Q!T))); + } + } +} + +/++ + Whether the given type is $(D char), $(D wchar), or $(D dchar), ignoring all + qualifiers. + + Note that this does not include implicit conversions or enum types. The + type itself must be one of the three built-in character type. + +/ +enum isSomeChar(T) = is(immutable T == immutable char) || + is(immutable T == immutable wchar) || + is(immutable T == immutable dchar); + +/// +@safe unittest +{ + // Some types which are character types. + static assert( isSomeChar!char); + static assert( isSomeChar!wchar); + static assert( isSomeChar!dchar); + static assert( isSomeChar!(const char)); + static assert( isSomeChar!(immutable char)); + static assert( isSomeChar!(inout wchar)); + static assert( isSomeChar!(shared wchar)); + static assert( isSomeChar!(const shared dchar)); + + static assert( isSomeChar!(typeof('c'))); + static assert( isSomeChar!(typeof("hello world"[3]))); + + dchar c; + static assert( isSomeChar!(typeof(c))); + + // Some types which aren't character types. + static assert(!isSomeChar!int); + static assert(!isSomeChar!byte); + static assert(!isSomeChar!string); + static assert(!isSomeChar!wstring); + static assert(!isSomeChar!dstring); + static assert(!isSomeChar!(char[4])); + + static struct S + { + dchar c; + } + static assert(!isSomeChar!S); + + // The struct itself isn't considered a character, + // but its member variable is when checked directly. + static assert( isSomeChar!(typeof(S.c))); + + enum E : dchar + { + a = 'a' + } + + // Enums do not count. + static assert(!isSomeChar!E); + + static struct AliasThis + { + dchar c; + alias this = c; + } + + // Other implicit conversions do not count. + static assert(!isSomeChar!AliasThis); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(char, wchar, dchar)) + { + enum E : Q!T { a = Q!T.init } + + static assert( isSomeChar!(Q!T)); + static assert(!isSomeChar!E); + static assert(!isSomeChar!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, + long, ulong, float, double, real, + char[], wchar[], dchar[], int[], void[], + char[12], wchar[17], dchar[2])) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isSomeChar!(Q!T)); + static assert(!isSomeChar!E); + static assert(!isSomeChar!(AliasThis!(Q!T))); + } + } +} + /++ Removes the outer layer of $(D const), $(D inout), or $(D immutable) from type $(D T). From ac6c03a646b950ddb6d1d608304ac93caef067aa Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 8 Mar 2024 16:08:47 +0000 Subject: [PATCH 10/57] [std.algorithm.searching] Improve `count` docs Fix `value` missing reference. Tweak wording. Explain `pred` better. Separate out needle overloads from the other 2. Fix 'Returns'. Split example into 2. --- std/algorithm/searching.d | 61 +++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 465723c16ed..99f318ca7e0 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -605,34 +605,27 @@ if (isNarrowString!R1 && isNarrowString!R2) // count /** -The first version counts the number of elements `x` in `r` for -which `pred(x, value)` is `true`. `pred` defaults to +The first version counts each element `e` in `haystack` for +which `pred(e, needle)` is `true`. `pred` defaults to equality. Performs $(BIGOH haystack.length) evaluations of `pred`. -The second version returns the number of times `needle` occurs in -`haystack`. Throws an exception if `needle.empty`, as the _count +The second version counts the number of times `needle` was matched in +`haystack`. `pred` compares elements in each range. +Throws an exception if `needle.empty`, as the _count of the empty range in any range would be infinite. Overlapped counts are not considered, for example `count("aaa", "aa")` is `1`, not `2`. -The third version counts the elements for which `pred(x)` is $(D -true). Performs $(BIGOH haystack.length) evaluations of `pred`. - -The fourth version counts the number of elements in a range. It is -an optimization for the third version: if the given range has the -`length` property the count is returned right away, otherwise -performs $(BIGOH haystack.length) to walk the range. - Note: Regardless of the overload, `count` will not accept infinite ranges for `haystack`. Params: - pred = The predicate to evaluate. + pred = The predicate to compare elements. haystack = The range to _count. - needle = The element or sub-range to _count in the `haystack`. + needle = The element or sub-range to _count in `haystack`. Returns: - The number of positions in the `haystack` for which `pred` returned true. + The number of matches in `haystack`. */ size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) if (isInputRange!Range && !isInfinite!Range && @@ -645,21 +638,22 @@ if (isInputRange!Range && !isInfinite!Range && /// @safe unittest { - import std.uni : toLower; - // count elements in range int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; - assert(count(a) == 9); assert(count(a, 2) == 3); assert(count!("a > b")(a, 2) == 5); +} + +/// +@safe unittest +{ + import std.uni : toLower; // count range in range assert(count("abcadfabf", "ab") == 2); assert(count("ababab", "abab") == 1); assert(count("ababab", "abx") == 0); // fuzzy count range in range assert(count!((a, b) => toLower(a) == toLower(b))("AbcAdFaBf", "ab") == 2); - // count predicate in range - assert(count!("a > 1")(a) == 8); } @safe unittest @@ -711,7 +705,22 @@ if (isForwardRange!R1 && !isInfinite!R1 && } } -/// Ditto +/** +The first version counts each element `e` in `haystack` for which `pred(e)` is $(D +true). Performs $(BIGOH haystack.length) evaluations of `pred`. + +The second version counts the number of elements in a range. +If the given range has the `length` property, +that is returned right away, otherwise +performs $(BIGOH haystack.length) to walk the range. + +Params: + pred = Optional predicate to compare elements. + haystack = The range to _count. + +Returns: + The number of elements in `haystack` (for which `pred` returned true). +*/ size_t count(alias pred, R)(R haystack) if (isInputRange!R && !isInfinite!R && is(typeof(unaryFun!pred(haystack.front)))) @@ -723,6 +732,16 @@ if (isInputRange!R && !isInfinite!R && return result; } +/// +@safe unittest +{ + // count elements in range + int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; + assert(count(a) == 9); + // count predicate in range + assert(count!("a > 2")(a) == 5); +} + /// Ditto size_t count(R)(R haystack) if (isInputRange!R && !isInfinite!R) From 6cbb729db6e40462cac6573e242beee08b6cdd2c Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 8 Mar 2024 12:19:04 -0700 Subject: [PATCH 11/57] Remove isSomeString and isSomeChar. (#8942) The consensus seems to be that these two should go, for better or worse. --- lib/sys/traits.d | 212 ----------------------------------------------- 1 file changed, 212 deletions(-) diff --git a/lib/sys/traits.d b/lib/sys/traits.d index a3834231970..0c3f838b558 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -53,8 +53,6 @@ $(TR $(TD Categories of types) $(TD $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF isDynamicArray) - $(LREF isSomeChar) - $(LREF isSomeString) $(LREF isStaticArray) )) $(TR $(TD Traits for removing type qualfiers) $(TD @@ -329,216 +327,6 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); } } -/++ - Whether the given type is a built-in string type - i.e whether it's a - dynamic array of $(D char), $(D wchar), or $(D dchar), ignoring all - qualifiers. - - Note that this does not include implicit conversions or enum types. The - type itself must be a dynamic array whose element type is one of the three - built-in character types. - +/ -enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar)); - -/// -@safe unittest -{ - // Some types which are string types. - static assert( isSomeString!string); - static assert( isSomeString!wstring); - static assert( isSomeString!dstring); - static assert( isSomeString!(char[])); - static assert( isSomeString!(wchar[])); - static assert( isSomeString!(dchar[])); - static assert( isSomeString!(const char[])); - static assert( isSomeString!(immutable char[])); - static assert( isSomeString!(inout wchar[])); - static assert( isSomeString!(shared wchar[])); - static assert( isSomeString!(const shared dchar[])); - - static assert( isSomeString!(typeof("aaa"))); - static assert( isSomeString!(typeof("aaa"w))); - static assert( isSomeString!(typeof("aaa"d))); - - string s; - static assert( isSomeString!(typeof(s))); - - // Some types which are not strings. - static assert(!isSomeString!int); - static assert(!isSomeString!(int[])); - static assert(!isSomeString!(byte[])); - - // Static arrays of characters are not considered strings. - static assert(!isSomeString!(char[4])); - - static struct S - { - string str; - } - static assert(!isSomeString!S); - - // The struct itself isn't considered a string, - // but its member variable is when checked directly. - static assert( isSomeString!(typeof(S.str))); - - // While strings can be null, typeof(null) is not typed as a string. - static assert(!isSomeString!(typeof(null))); - - // However, naturally, if null is cast to a string type, - // it's a string type, since the cast forces the type. - static assert( isSomeString!(typeof(cast(char[]) null))); - - enum E : string - { - a = "dlang" - } - - // Enums do not count. - static assert(!isSomeString!E); - - static struct AliasThis - { - string str; - alias this = str; - } - - // Other implicit conversions do not count. - static assert(!isSomeString!AliasThis); -} - -@safe unittest -{ - import lib.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(char[], wchar[], dchar[])) - { - enum E : Q!T { a = Q!T.init } - - static assert( isSomeString!(Q!T)); - static assert(!isSomeString!E); - static assert(!isSomeString!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(char, wchar, dchar, int, byte[], ubyte[], int[], char[12], wchar[17], dchar[2], void[])) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isSomeString!(Q!T)); - static assert(!isSomeString!E); - static assert(!isSomeString!(AliasThis!(Q!T))); - } - } -} - -/++ - Whether the given type is $(D char), $(D wchar), or $(D dchar), ignoring all - qualifiers. - - Note that this does not include implicit conversions or enum types. The - type itself must be one of the three built-in character type. - +/ -enum isSomeChar(T) = is(immutable T == immutable char) || - is(immutable T == immutable wchar) || - is(immutable T == immutable dchar); - -/// -@safe unittest -{ - // Some types which are character types. - static assert( isSomeChar!char); - static assert( isSomeChar!wchar); - static assert( isSomeChar!dchar); - static assert( isSomeChar!(const char)); - static assert( isSomeChar!(immutable char)); - static assert( isSomeChar!(inout wchar)); - static assert( isSomeChar!(shared wchar)); - static assert( isSomeChar!(const shared dchar)); - - static assert( isSomeChar!(typeof('c'))); - static assert( isSomeChar!(typeof("hello world"[3]))); - - dchar c; - static assert( isSomeChar!(typeof(c))); - - // Some types which aren't character types. - static assert(!isSomeChar!int); - static assert(!isSomeChar!byte); - static assert(!isSomeChar!string); - static assert(!isSomeChar!wstring); - static assert(!isSomeChar!dstring); - static assert(!isSomeChar!(char[4])); - - static struct S - { - dchar c; - } - static assert(!isSomeChar!S); - - // The struct itself isn't considered a character, - // but its member variable is when checked directly. - static assert( isSomeChar!(typeof(S.c))); - - enum E : dchar - { - a = 'a' - } - - // Enums do not count. - static assert(!isSomeChar!E); - - static struct AliasThis - { - dchar c; - alias this = c; - } - - // Other implicit conversions do not count. - static assert(!isSomeChar!AliasThis); -} - -@safe unittest -{ - import lib.sys.meta : Alias, AliasSeq; - - static struct AliasThis(T) - { - T member; - alias this = member; - } - - foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) - { - foreach (T; AliasSeq!(char, wchar, dchar)) - { - enum E : Q!T { a = Q!T.init } - - static assert( isSomeChar!(Q!T)); - static assert(!isSomeChar!E); - static assert(!isSomeChar!(AliasThis!(Q!T))); - } - - foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, - long, ulong, float, double, real, - char[], wchar[], dchar[], int[], void[], - char[12], wchar[17], dchar[2])) - { - enum E : Q!T { a = Q!T.init } - - static assert(!isSomeChar!(Q!T)); - static assert(!isSomeChar!E); - static assert(!isSomeChar!(AliasThis!(Q!T))); - } - } -} - /++ Removes the outer layer of $(D const), $(D inout), or $(D immutable) from type $(D T). From a44abe91674ebafe4d4c137197b13b2d6cdc05d4 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 8 Mar 2024 11:00:40 -0800 Subject: [PATCH 12/57] use cast() where we can --- std/bitmanip.d | 8 ++++---- std/conv.d | 6 +++--- std/math/algebraic.d | 4 ++-- std/variant.d | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/std/bitmanip.d b/std/bitmanip.d index c9813e35476..3006d6337ad 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -2959,10 +2959,10 @@ if (__traits(isIntegral, T)) Unqual!T result; version (LittleEndian) foreach_reverse (b; array) - result = cast(Unqual!T) ((result << 8) | b); + result = cast() cast(T) ((result << 8) | b); else foreach (b; array) - result = cast(Unqual!T) ((result << 8) | b); + result = cast() cast(T) ((result << 8) | b); return cast(T) result; } @@ -2977,7 +2977,7 @@ if (__traits(isIntegral, T)) foreach (i; 0 .. T.sizeof) { result[i] = cast(ubyte) tmp; - tmp = cast(Unqual!T) (tmp >>> 8); + tmp = cast() cast(T) (tmp >>> 8); } } else @@ -2985,7 +2985,7 @@ if (__traits(isIntegral, T)) foreach_reverse (i; 0 .. T.sizeof) { result[i] = cast(ubyte) tmp; - tmp = cast(Unqual!T) (tmp >>> 8); + tmp = cast()(T) (tmp >>> 8); } } return result; diff --git a/std/conv.d b/std/conv.d index 5d02df08bf9..3aa73c68bf1 100644 --- a/std/conv.d +++ b/std/conv.d @@ -5250,7 +5250,7 @@ if (isIntegral!T && isOutputRange!(W, char)) auto unsigned(T)(T x) if (isIntegral!T) { - return cast(Unqual!(Unsigned!T))x; + return cast() cast(Unsigned!T) x; } /// @@ -5271,7 +5271,7 @@ if (isSomeChar!T) { // All characters are unsigned static assert(T.min == 0, T.stringof ~ ".min must be zero"); - return cast(Unqual!T) x; + return cast() x; } @safe unittest @@ -5328,7 +5328,7 @@ if (isSomeChar!T) auto signed(T)(T x) if (isIntegral!T) { - return cast(Unqual!(Signed!T))x; + return cast() cast(Signed!T) x; } /// diff --git a/std/math/algebraic.d b/std/math/algebraic.d index fd305231d08..cfb88c89f75 100644 --- a/std/math/algebraic.d +++ b/std/math/algebraic.d @@ -974,9 +974,9 @@ private T powIntegralImpl(PowType type, T)(T val) else { static if (isSigned!T) - return cast(Unqual!T) (val < 0 ? -(T(1) << bsr(0 - val) + type) : T(1) << bsr(val) + type); + return cast() cast(T) (val < 0 ? -(T(1) << bsr(0 - val) + type) : T(1) << bsr(val) + type); else - return cast(Unqual!T) (T(1) << bsr(val) + type); + return cast() cast(T) (T(1) << bsr(val) + type); } } diff --git a/std/variant.d b/std/variant.d index 41cd4848b12..f7832104d70 100644 --- a/std/variant.d +++ b/std/variant.d @@ -382,7 +382,7 @@ private: static if (isStaticArray!A && isDynamicArray!T) { auto this_ = (*src)[]; - emplaceRef(*cast(Unqual!T*) zat, cast(Unqual!T) this_); + emplaceRef(*cast(Unqual!T*) zat, cast() cast(T) this_); } else { From 2759bc800a5ec7db0ea393c21e347e9432bf3e81 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 8 Mar 2024 13:23:10 -0800 Subject: [PATCH 13/57] utf.d replace use of Unqual with cast() --- std/utf.d | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/std/utf.d b/std/utf.d index 3eef5cbcc03..c0cd3863b32 100644 --- a/std/utf.d +++ b/std/utf.d @@ -328,7 +328,7 @@ Returns: bool isValidCodepoint(Char)(Char c) if (isSomeChar!Char) { - alias UChar = Unqual!Char; + alias UChar = typeof(cast() c); static if (is(UChar == char)) { return c <= 0x7F; @@ -1418,8 +1418,8 @@ do } else { - alias Char = Unqual!(ElementType!S); - Char[4] codeUnits; + alias Char = typeof(cast() ElementType!S.init); + Char[4] codeUnits = void; S tmp = str.save; for (size_t i = numCodeUnits; i > 0; ) { @@ -2821,7 +2821,7 @@ if (isSomeChar!C) size_t codeLength(C, InputRange)(InputRange input) if (isSomeFiniteCharInputRange!InputRange) { - alias EncType = Unqual!(ElementEncodingType!InputRange); + alias EncType = typeof(cast() ElementEncodingType!InputRange.init); static if (isSomeString!InputRange && is(EncType == C) && is(typeof(input.length))) return input.length; else @@ -3089,7 +3089,8 @@ private T toUTFImpl(T, S)(scope S s) static if (is(S == C[], C) || hasLength!S) app.reserve(s.length); - foreach (c; s.byUTF!(Unqual!(ElementEncodingType!T))) + ElementEncodingType!T e = void; + foreach (c; s.byUTF!(typeof(cast() ElementEncodingType!T.init))) app.put(c); return app.data; @@ -3168,10 +3169,10 @@ if (is(immutable typeof(*P.init) == typeof(str[0]))) return trustedPtr(); } - alias C = Unqual!(ElementEncodingType!S); + alias C = typeof(cast() ElementEncodingType!S.init); //If the P is mutable, then we have to make a copy. - static if (is(Unqual!(typeof(*P.init)) == typeof(*P.init))) + static if (is(typeof(cast() *P.init) == typeof(*P.init))) { return toUTFzImpl!(P, const(C)[])(cast(const(C)[])str); } @@ -3203,13 +3204,15 @@ private P toUTFzImpl(P, S)(return scope S str) @safe pure if (is(typeof(str[0]) C) && is(immutable typeof(*P.init) == immutable C) && !is(C == immutable)) //C[] or const(C)[] -> C*, const(C)*, or immutable(C)* { - alias InChar = typeof(str[0]); - alias OutChar = typeof(*P.init); + alias InChar = typeof(str[0]); + alias UInChar = typeof(cast() str[0]); // unqualified version of InChar + alias OutChar = typeof(*P.init); + alias UOutChar = typeof(cast() *P.init); // unqualified version //const(C)[] -> const(C)* or //C[] -> C* or const(C)* - static if (( is(const(Unqual!InChar) == InChar) && is(const(Unqual!OutChar) == OutChar)) || - (!is(const(Unqual!InChar) == InChar) && !is(immutable(Unqual!OutChar) == OutChar))) + static if (( is(const(UInChar) == InChar) && is( const(UOutChar) == OutChar)) || + (!is(const(UInChar) == InChar) && !is(immutable(UOutChar) == OutChar))) { if (!__ctfe) { @@ -3228,7 +3231,7 @@ if (is(typeof(str[0]) C) && is(immutable typeof(*P.init) == immutable C) && !is( else { import std.array : uninitializedArray; - auto copy = uninitializedArray!(Unqual!OutChar[])(str.length + 1); + auto copy = uninitializedArray!(UOutChar[])(str.length + 1); copy[0 .. $ - 1] = str[]; copy[$ - 1] = '\0'; From 84c6e78a76824bdfb67453efee9d056991294322 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 9 Mar 2024 15:55:42 -0700 Subject: [PATCH 14/57] Add isInteger and isFloatingPoint to lib.sys.traits. (#8945) isInteger is similar to std.traits.isIntegral, but I went with a different name because it's specifically testing for the built-in integer types and not for all types which are even vaguely integral. It also helps distinguish from __traits(isIntegral, T) which accepts just about anything and its grandmother which is even vaguely integer-like / integral (to the point that I honestly question how much sense it makes to ever use it). The main difference between isInteger and std.traits.isIntegral is that isIntegral accepts enums, whereas isInteger does not. isIntegral also theoretically accepts cent and ucent, but since it's now an error to use those, I don't think that it accepts them any longer in practice anyway. isInteger makes no attempt to accept them and does not accept their replacement types, because they're not built-in (and it's not clear to me that we've fully sorted out what we're doing with those anyway). isFloatingPoint is similar to std.traits' isFloatingPoint except that the std.traits version accepts everything that __traits(isFloating, T) does along with any type that implicitly converts to real. The lib.sys.traits version accepts float, double, and real only. --- lib/sys/traits.d | 300 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) diff --git a/lib/sys/traits.d b/lib/sys/traits.d index 0c3f838b558..798437e8e31 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -53,6 +53,8 @@ $(TR $(TD Categories of types) $(TD $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF isDynamicArray) + $(LREF isInteger) + $(LREF isFloatingPoint) $(LREF isStaticArray) )) $(TR $(TD Traits for removing type qualfiers) $(TD @@ -500,6 +502,304 @@ template Unshared(T) static assert(is(Unshared!(shared(Foo!int)) == Foo!int)); } +/++ + Whether the given type is one of the built-in integer types, ignoring all + qualifiers. + + $(TABLE + $(TR $(TH Integer Types)) + $(TR $(TD byte)) + $(TR $(TD ubyte)) + $(TR $(TD short)) + $(TR $(TD ushort)) + $(TR $(TD int)) + $(TR $(TD uint)) + $(TR $(TD long)) + $(TR $(TD ulong)) + ) + + Note that this does not include implicit conversions or enum types. The + type itself must be one of the built-in integer types. + + This trait does have some similarities to $(D __traits(isIntegral, T)), but + $(D isIntegral) accepts a $(D lot) more types than isInteger does. + isInteger is specifically for testing for the built-in integer types, + whereas $(D isIntegral) tests for a whole set of types that are vaguely + integer-like (including $(D bool), the three built-in character types, and + some of the vector types from core.simd). So, for most code, isInteger is + going to be more appropriate, but obviously, it depends on what the code is + trying to do. + + See also: + $(DDSUBLINK spec/traits, isIntegral, $(D __traits(isIntegral, T))) + +/ +enum isInteger(T) = is(immutable T == immutable byte) || + is(immutable T == immutable ubyte) || + is(immutable T == immutable short) || + is(immutable T == immutable ushort) || + is(immutable T == immutable int) || + is(immutable T == immutable uint) || + is(immutable T == immutable long) || + is(immutable T == immutable ulong); + +/// +@safe unittest +{ + // Some types which are integer types. + static assert( isInteger!byte); + static assert( isInteger!ubyte); + static assert( isInteger!short); + static assert( isInteger!ushort); + static assert( isInteger!int); + static assert( isInteger!uint); + static assert( isInteger!long); + static assert( isInteger!ulong); + + static assert( isInteger!(const ubyte)); + static assert( isInteger!(immutable short)); + static assert( isInteger!(inout int)); + static assert( isInteger!(shared uint)); + static assert( isInteger!(const shared ulong)); + + static assert( isInteger!(typeof(42))); + static assert( isInteger!(typeof(1234567890L))); + + int i; + static assert( isInteger!(typeof(i))); + + // Some types which aren't integer types. + static assert(!isInteger!bool); + static assert(!isInteger!char); + static assert(!isInteger!wchar); + static assert(!isInteger!dchar); + static assert(!isInteger!(int[])); + static assert(!isInteger!(ubyte[4])); + static assert(!isInteger!(int*)); + static assert(!isInteger!double); + static assert(!isInteger!string); + + static struct S + { + int i; + } + static assert(!isInteger!S); + + // The struct itself isn't considered an integer, + // but its member variable is when checked directly. + static assert( isInteger!(typeof(S.i))); + + enum E : int + { + a = 42 + } + + // Enums do not count. + static assert(!isInteger!E); + + static struct AliasThis + { + int i; + alias this = i; + } + + // Other implicit conversions do not count. + static assert(!isInteger!AliasThis); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + // The actual core.simd types available vary from system to system, so we + // have to be a bit creative here. The reason that we're testing these types + // is because __traits(isIntegral, T) accepts them, but isInteger is not + // supposed to. + template SIMDTypes() + { + import core.simd; + + alias SIMDTypes = AliasSeq!(); + static if (is(ubyte16)) + SIMDTypes = AliasSeq!(SIMDTypes, ubyte16); + static if (is(int4)) + SIMDTypes = AliasSeq!(SIMDTypes, int4); + static if (is(double2)) + SIMDTypes = AliasSeq!(SIMDTypes, double2); + static if (is(void16)) + SIMDTypes = AliasSeq!(SIMDTypes, void16); + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) + { + enum E : Q!T { a = Q!T.init } + + static assert( isInteger!(Q!T)); + static assert(!isInteger!E); + static assert(!isInteger!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(bool, char, wchar, dchar, float, double, real, SIMDTypes!(), + int[], ubyte[8], dchar[], void[], long*)) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isInteger!(Q!T)); + static assert(!isInteger!E); + static assert(!isInteger!(AliasThis!(Q!T))); + } + } +} + +/++ + Whether the given type is one of the built-in floating-point types, ignoring + all qualifiers. + + $(TABLE + $(TR $(TH Floating-Point Types)) + $(TR $(TD float)) + $(TR $(TD double)) + $(TR $(TD real)) + ) + + Note that this does not include implicit conversions or enum types. The + type itself must be one of the built-in floating-point types. + + This trait does have some similarities to $(D __traits(isFloating, T)), but + $(D isFloating) accepts more types than isFloatingPoint does. + isFloatingPoint is specifically for testing for the built-in floating-point + types, whereas $(D isFloating) tests for a whole set of types that are + vaguely float-like (including enums with a base type which is a + floating-point type and some of the vector types from core.simd). So, for + most code, isFloatingPoint is going to be more appropriate, but obviously, + it depends on what the code is trying to do. + + See also: + $(DDSUBLINK spec/traits, isFloating, $(D __traits(isFloating, T))) + +/ +enum isFloatingPoint(T) = is(immutable T == immutable float) || + is(immutable T == immutable double) || + is(immutable T == immutable real); + +/// +@safe unittest +{ + // Some types which are floating-point types. + static assert( isFloatingPoint!float); + static assert( isFloatingPoint!double); + static assert( isFloatingPoint!real); + + static assert( isFloatingPoint!(const float)); + static assert( isFloatingPoint!(immutable float)); + static assert( isFloatingPoint!(inout double)); + static assert( isFloatingPoint!(shared double)); + static assert( isFloatingPoint!(const shared real)); + + static assert( isFloatingPoint!(typeof(42.0))); + static assert( isFloatingPoint!(typeof(42f))); + static assert( isFloatingPoint!(typeof(1e5))); + static assert( isFloatingPoint!(typeof(97.4L))); + + double d; + static assert( isFloatingPoint!(typeof(d))); + + // Some types which aren't floating-point types. + static assert(!isFloatingPoint!bool); + static assert(!isFloatingPoint!char); + static assert(!isFloatingPoint!dchar); + static assert(!isFloatingPoint!int); + static assert(!isFloatingPoint!long); + static assert(!isFloatingPoint!(float[])); + static assert(!isFloatingPoint!(double[4])); + static assert(!isFloatingPoint!(real*)); + static assert(!isFloatingPoint!string); + + static struct S + { + double d; + } + static assert(!isFloatingPoint!S); + + // The struct itself isn't considered a floating-point type, + // but its member variable is when checked directly. + static assert( isFloatingPoint!(typeof(S.d))); + + enum E : double + { + a = 12.34 + } + + // Enums do not count. + static assert(!isFloatingPoint!E); + + static struct AliasThis + { + double d; + alias this = d; + } + + // Other implicit conversions do not count. + static assert(!isFloatingPoint!AliasThis); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + // The actual core.simd types available vary from system to system, so we + // have to be a bit creative here. The reason that we're testing these types + // is because __traits(isFloating, T) accepts them, but isFloatingPoint is + // not supposed to. + template SIMDTypes() + { + import core.simd; + + alias SIMDTypes = AliasSeq!(); + static if (is(int4)) + SIMDTypes = AliasSeq!(SIMDTypes, int4); + static if (is(double2)) + SIMDTypes = AliasSeq!(SIMDTypes, double2); + static if (is(void16)) + SIMDTypes = AliasSeq!(SIMDTypes, void16); + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(float, double, real)) + { + enum E : Q!T { a = Q!T.init } + + static assert( isFloatingPoint!(Q!T)); + static assert(!isFloatingPoint!E); + static assert(!isFloatingPoint!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(bool, char, wchar, dchar, byte, ubyte, short, ushort, + int, uint, long, ulong, SIMDTypes!(), + int[], float[8], real[], void[], double*)) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isFloatingPoint!(Q!T)); + static assert(!isFloatingPoint!E); + static assert(!isFloatingPoint!(AliasThis!(Q!T))); + } + } +} + /++ Removes the outer layer of all type qualifiers from type $(D T). From b37c7cdcfc8448c3101e34434996928dc2d4cef4 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Tue, 12 Mar 2024 04:54:56 -0600 Subject: [PATCH 15/57] Add isNumeric and isPointer. (#8946) isNumeric!T is equivalent to isInteger!T || isFloatingPoint!T, so I suppose that its value is somewhat questionable with how simple it is, but it does save a template instantiation in the process and arguably is clearer for documentation purposes. There is an isNumeric in std.traits, but I would have to study it in detail to figure out exactly what it accepts, because it is written in a rather confusing manner (though it does clearly accept enums, which this isNumeric does not). I have tried to make lib.sys.traits' isNumeric very clear in its documentation about what it accepts. isPointer does exactly what it says on the tin, so it's quite straightforward all around. I also beefed up the documentation on isDynamicArray to discuss a bit about dynamic arrays vs slices and why the kind of memory that backs a dynamic array is irrelevant to the type (and thus irrelevant to the trait) - as well as pointing out that they're completely different from pointers, if that isn't obvious enough. --- lib/sys/traits.d | 360 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 358 insertions(+), 2 deletions(-) diff --git a/lib/sys/traits.d b/lib/sys/traits.d index 798437e8e31..9fccfe3e906 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -53,8 +53,10 @@ $(TR $(TD Categories of types) $(TD $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF isDynamicArray) - $(LREF isInteger) $(LREF isFloatingPoint) + $(LREF isInteger) + $(LREF isNumeric) + $(LREF isPointer) $(LREF isStaticArray) )) $(TR $(TD Traits for removing type qualfiers) $(TD @@ -86,10 +88,78 @@ module lib.sys.traits; /++ - Whether type $(D T) is a dynamic array. + Whether the given type is a dynamic array (or what is sometimes referred to + as a slice, since a dynamic array in D is a slice of memory). Note that this does not include implicit conversions or enum types. The type itself must be a dynamic array. + + Remember that D's dynamic arrays are essentially: + --- + struct DynamicArray(T) + { + size_t length; + T* ptr; + } + --- + where $(D ptr) points to the first element in the array, and $(D length) is + the number of elements in the array. + + A dynamic array is not a pointer (unlike arrays in C/C++), and its elements + do not live inside the dynamic array itself. The dynamic array is simply a + slice of memory and does not own or manage its own memory. It can be a + slice of any piece of memory, including GC-allocated memory, the stack, + malloc-ed memory, etc. (with what kind of memory it is of course being + determined by how the dynamic array was created in the first place) + - though if you do any operations on it which end up requiring allocation + (e.g. appending to it if it doesn't have the capacity to expand in-place, + which it won't if it isn't a slice of GC-allocated memory), then that + reallocation will result in the dynamic array being a slice of newly + allocated, GC-backed memory (regardless of what it was a slice of before), + since it's the GC that deals with those allocations. + + As long as code just accesses the elements or members of the dynamic array + - or reduces its length so that it's a smaller slice - it will continue to + point to whatever block of memory it pointed to originally. And because the + GC makes sure that appending to a dynamic array does not stomp on the + memory of any other dynamic arrays, appending to a dynamic array will not + affect any other dynamic array which is a slice of that same block of + memory whether a reallocation occurs or not. + + Regardless, since what allocated the memory that the dynamic array is a + slice of is irrevelant to the type of the dynamic array, whether a given + type is a dynamic array has nothing to do with the kind of memory that's + backing it. A dynamic array which is a slice of a static array of $(D int) + is the the same type as a dynamic array of $(D int) allocated with $(D new) + - i.e. both are $(D int[]). So, this trait will not tell you anything about + what kind of memory a dynamic array is a slice of. It just tells you + whether the type is a dynamic array or not. + + If for some reason, it matters for a function what kind of memory backs one + of its parameters which is a dynamic array, or it needs to be made clear + whether the function will possibly cause that dynamic array to be + reallocated, then that needs to be indicated by the documentation and + cannot be enforced with a template constraint. A template constraint can + enforce that a type used with a template meets certain criteria (e.g. that + it's a dynamic array), but it cannot enforce anything about how the + template actually uses the type. + + However, it $(D is) possible to enforce that a function doesn't use any + operations on a dynamic array which might cause it to be reallocated by + marking that function as $(D @nogc). + + In most cases though, code can be written to not care what kind of memory + backs a dynamic array, because none of the operations on a dynamic array + actually care what kind of memory it's a slice of. It mostly just matters + when you need to track the lifetime of the memory, because it wasn't + allocated by the GC, or when it matters whether a dynamic array could be + reallocated or not (e.g. because the code needs to have that dynamic array + continue to point to the same block of memory). + + See_Also: + $(LREF isPointer) + $(LREF isStaticArray) + $(DDSUBLINK spec/arrays, , The language spec for arrays) +/ enum isDynamicArray(T) = is(T == U[], U); @@ -131,6 +201,10 @@ enum isDynamicArray(T) = is(T == U[], U); int[2] sArr = [42, 97]; static assert(!isDynamicArray!(typeof(sArr))); + // While a static array is not a dynamic array, + // a slice of a static array is a dynamic array. + static assert( isDynamicArray!(typeof(sArr[]))); + // Dynamic array of static arrays. static assert( isDynamicArray!(long[3][])); @@ -216,6 +290,7 @@ enum isDynamicArray(T) = is(T == U[], U); See also: $(DDSUBLINK spec/traits, isStaticArray, $(D __traits(isStaticArray, T))) + $(DDSUBLINK spec/arrays, , The language spec for arrays) +/ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); @@ -260,6 +335,10 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); int[] arr; static assert(!isStaticArray!(typeof(arr))); + // A slice of a static array is of course not a static array, + // because it's a dynamic array. + static assert(!isStaticArray!(typeof(sArr[]))); + // Static array of dynamic arrays. static assert( isStaticArray!(long[][3])); @@ -532,6 +611,8 @@ template Unshared(T) See also: $(DDSUBLINK spec/traits, isIntegral, $(D __traits(isIntegral, T))) + $(LREF isFloatingPoint) + $(LREF isNumeric) +/ enum isInteger(T) = is(immutable T == immutable byte) || is(immutable T == immutable ubyte) || @@ -683,6 +764,8 @@ enum isInteger(T) = is(immutable T == immutable byte) || See also: $(DDSUBLINK spec/traits, isFloating, $(D __traits(isFloating, T))) + $(LREF isInteger) + $(LREF isNumeric) +/ enum isFloatingPoint(T) = is(immutable T == immutable float) || is(immutable T == immutable double) || @@ -800,6 +883,279 @@ enum isFloatingPoint(T) = is(immutable T == immutable float) || } } +/++ + Whether the given type is one of the built-in numeric types, ignoring all + qualifiers. It's equivalent to $(D isInteger!T || isFloatingPoint!T), but + it only involves a single template instantation instead of two. + + $(TABLE + $(TR $(TH Numeric Types)) + $(TR $(TD byte)) + $(TR $(TD ubyte)) + $(TR $(TD short)) + $(TR $(TD ushort)) + $(TR $(TD int)) + $(TR $(TD uint)) + $(TR $(TD long)) + $(TR $(TD ulong)) + $(TR $(TD float)) + $(TR $(TD double)) + $(TR $(TD real)) + ) + + Note that this does not include implicit conversions or enum types. The + type itself must be one of the built-in numeric types. + + See_Also: + $(LREF isFloatingPoint) + $(LREF isInteger) + +/ +enum isNumeric(T) = is(immutable T == immutable byte) || + is(immutable T == immutable ubyte) || + is(immutable T == immutable short) || + is(immutable T == immutable ushort) || + is(immutable T == immutable int) || + is(immutable T == immutable uint) || + is(immutable T == immutable long) || + is(immutable T == immutable ulong) || + is(immutable T == immutable float) || + is(immutable T == immutable double) || + is(immutable T == immutable real); + +/// +@safe unittest +{ + // Some types which are numeric types. + static assert( isNumeric!byte); + static assert( isNumeric!ubyte); + static assert( isNumeric!short); + static assert( isNumeric!ushort); + static assert( isNumeric!int); + static assert( isNumeric!uint); + static assert( isNumeric!long); + static assert( isNumeric!ulong); + static assert( isNumeric!float); + static assert( isNumeric!double); + static assert( isNumeric!real); + + static assert( isNumeric!(const short)); + static assert( isNumeric!(immutable int)); + static assert( isNumeric!(inout uint)); + static assert( isNumeric!(shared long)); + static assert( isNumeric!(const shared real)); + + static assert( isNumeric!(typeof(42))); + static assert( isNumeric!(typeof(1234657890L))); + static assert( isNumeric!(typeof(42.0))); + static assert( isNumeric!(typeof(42f))); + static assert( isNumeric!(typeof(1e5))); + static assert( isNumeric!(typeof(97.4L))); + + int i; + static assert( isNumeric!(typeof(i))); + + // Some types which aren't numeric types. + static assert(!isNumeric!bool); + static assert(!isNumeric!char); + static assert(!isNumeric!dchar); + static assert(!isNumeric!(int[])); + static assert(!isNumeric!(double[4])); + static assert(!isNumeric!(real*)); + static assert(!isNumeric!string); + + static struct S + { + int i; + } + static assert(!isNumeric!S); + + // The struct itself isn't considered a numeric type, + // but its member variable is when checked directly. + static assert( isNumeric!(typeof(S.i))); + + enum E : int + { + a = 42 + } + + // Enums do not count. + static assert(!isNumeric!E); + + static struct AliasThis + { + int i; + alias this = i; + } + + // Other implicit conversions do not count. + static assert(!isNumeric!AliasThis); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + // The actual core.simd types available vary from system to system, so we + // have to be a bit creative here. The reason that we're testing these types + // is because __traits(isInteger, T) and __traits(isFloating, T) accept + // them, but isNumeric is not supposed to. + template SIMDTypes() + { + import core.simd; + + alias SIMDTypes = AliasSeq!(); + static if (is(int4)) + SIMDTypes = AliasSeq!(SIMDTypes, int4); + static if (is(double2)) + SIMDTypes = AliasSeq!(SIMDTypes, double2); + static if (is(void16)) + SIMDTypes = AliasSeq!(SIMDTypes, void16); + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real)) + { + enum E : Q!T { a = Q!T.init } + + static assert( isNumeric!(Q!T)); + static assert(!isNumeric!E); + static assert(!isNumeric!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(bool, char, wchar, dchar, SIMDTypes!(), + int[], float[8], real[], void[], double*)) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isNumeric!(Q!T)); + static assert(!isNumeric!E); + static assert(!isNumeric!(AliasThis!(Q!T))); + } + } +} + +/++ + Whether the given type is a pointer. + + Note that this does not include implicit conversions or enum types. The + type itself must be a pointer. + + Also, remember that unlike C/C++, D's arrays are not pointers. Rather, a + dynamic array in D is a slice of memory which has a member which is a + pointer to its first element and another member which is the length of the + array as $(D size_t). So, a dynamic array / slice has a $(D ptr) member + which is a pointer, but the dynamic array itself is not a pointer. + + See_Also: + $(LREF isDynamicArray) + +/ +enum isPointer(T) = is(T == U*, U); + +/// +@system unittest +{ + // Some types which are pointers. + static assert( isPointer!(bool*)); + static assert( isPointer!(int*)); + static assert( isPointer!(int**)); + static assert( isPointer!(real*)); + static assert( isPointer!(string*)); + + static assert( isPointer!(const int*)); + static assert( isPointer!(immutable int*)); + static assert( isPointer!(inout int*)); + static assert( isPointer!(shared int*)); + static assert( isPointer!(const shared int*)); + + static assert( isPointer!(typeof("foobar".ptr))); + + int* ptr; + static assert( isPointer!(typeof(ptr))); + + int i; + static assert( isPointer!(typeof(&i))); + + // Some types which aren't pointers. + static assert(!isPointer!bool); + static assert(!isPointer!int); + static assert(!isPointer!dchar); + static assert(!isPointer!(int[])); + static assert(!isPointer!(double[4])); + static assert(!isPointer!string); + + static struct S + { + int* ptr; + } + static assert(!isPointer!S); + + // The struct itself isn't considered a numeric type, + // but its member variable is when checked directly. + static assert( isPointer!(typeof(S.ptr))); + + enum E : immutable(char*) + { + a = "foobar".ptr + } + + // Enums do not count. + static assert(!isPointer!E); + + static struct AliasThis + { + int* ptr; + alias this = ptr; + } + + // Other implicit conversions do not count. + static assert(!isPointer!AliasThis); +} + +@safe unittest +{ + import lib.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + static struct S + { + int i; + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(long*, S*, S**, S***, double[]*)) + { + enum E : Q!T { a = Q!T.init } + + static assert( isPointer!(Q!T)); + static assert(!isPointer!E); + static assert(!isPointer!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(bool, char, wchar, dchar, byte, int, uint, long, + int[], float[8], real[], void[])) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isPointer!(Q!T)); + static assert(!isPointer!E); + static assert(!isPointer!(AliasThis!(Q!T))); + } + } +} + /++ Removes the outer layer of all type qualifiers from type $(D T). From e568c67f380af6bc6f51a6bf8ed251cfe2bdbfe7 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Tue, 12 Mar 2024 20:25:52 -0600 Subject: [PATCH 16/57] Rearrange some code in lib.sys.traits. Actually organizing all of the symbols within a file is pretty much a losing game in general, but I was trying to put the is* traits together and accidentally put some in the section containing Unconst, Unshared, and Unshared. So, this just moves Unconst and Unshared so that they're next to Unqual again. There is zero code that's actually changed. It's just moved within the file. --- lib/sys/traits.d | 346 +++++++++++++++++++++++------------------------ 1 file changed, 173 insertions(+), 173 deletions(-) diff --git a/lib/sys/traits.d b/lib/sys/traits.d index 9fccfe3e906..3768ea040af 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -408,179 +408,6 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); } } -/++ - Removes the outer layer of $(D const), $(D inout), or $(D immutable) - from type $(D T). - - If none of those qualifiers have been applied to the outer layer of - type $(D T), then the result is $(D T). - - Due to limitations with D's type system, user-defined types have the type - qualifier removed entirely if present. The types of the member variables - themselves are unaffected beyond how removing the type qualifier from the - type containing them affects them (e.g. an $(D int*) member that is - $(D const(int*)) because the type containing it is $(D const) becomes - $(D int*) when Unconst is used on the containing type, because $(D const) - is removed from the containing type. The member does not become - $(D const(int)*) as would occur if Unconst were used directly on a - $(D const(int*))). - - Also, Unconst has no effect on what a templated type is instantiated with, - so if a templated type is instantiated with a template argument which is a - const type, the template instantiation will not change. - +/ -version (StdDdoc) template Unconst(T) -{ - import core.internal.traits : CoreUnconst = Unconst; - alias Unconst = CoreUnconst!T; -} -else -{ - import core.internal.traits : CoreUnconst = Unconst; - alias Unconst = CoreUnconst; -} - -/// -@safe unittest -{ - static assert(is(Unconst!( int) == int)); - static assert(is(Unconst!( const int) == int)); - static assert(is(Unconst!( inout int) == int)); - static assert(is(Unconst!( inout const int) == int)); - static assert(is(Unconst!(shared int) == shared int)); - static assert(is(Unconst!(shared const int) == shared int)); - static assert(is(Unconst!(shared inout int) == shared int)); - static assert(is(Unconst!(shared inout const int) == shared int)); - static assert(is(Unconst!( immutable int) == int)); - - // Only the outer layer of immutable is removed. - // immutable(int[]) -> immutable(int)[] - alias ImmIntArr = immutable(int[]); - static assert(is(Unconst!ImmIntArr == immutable(int)[])); - - // Only the outer layer of const is removed. - // immutable(int*) -> immutable(int)* - alias ConstIntPtr = const(int*); - static assert(is(Unconst!ConstIntPtr == const(int)*)); - - // const(int)* -> const(int)* - alias PtrToConstInt = const(int)*; - static assert(is(Unconst!PtrToConstInt == const(int)*)); - - static struct S - { - int* ptr; - } - - const S s; - static assert(is(typeof(s) == const S)); - static assert(is(typeof(typeof(s).ptr) == const int*)); - - // For user-defined types, the const qualifier is removed entirely. - // const S -> S - static assert(is(Unconst!(typeof(s)) == S)); - static assert(is(typeof(Unconst!(typeof(s)).ptr) == int*)); - - static struct Foo(T) - { - T* ptr; - } - - // The qualifer on the type is removed, but the qualifier on the template - // argument is not. - static assert(is(Unconst!(const(Foo!(const int))) == Foo!(const int))); - static assert(is(Unconst!(Foo!(const int)) == Foo!(const int))); - static assert(is(Unconst!(const(Foo!int)) == Foo!int)); -} - -/++ - Removes the outer layer of $(D shared) from type $(D T). - - If $(D shared) has not been applied to the outer layer of type $(D T), then - the result is $(D T). - - Note that while $(D immutable) is implicitly $(D shared), it is unaffected - by Unshared. Only explict $(D shared) is removed. - - Due to limitations with D's type system, user-defined types have the type - qualifier removed entirely if present. The types of the member variables - themselves are unaffected beyond how removing the type qualifier from the - type containing them affects them (e.g. an $(D int*) member that is - $(D shared(int*)) because the type containing it is $(D shared) becomes - $(D int*) when Unshared is used on the containing type, because $(D shared) - is removed from the containing type. The member does not become - $(D shared(int)*) as would occur if Unshared were used directly on a - $(D shared(int*))). - - Also, Unshared has no effect on what a templated type is instantiated with, - so if a templated type is instantiated with a template argument which is a - shared type, the template instantiation will not change. - +/ -template Unshared(T) -{ - static if (is(T == shared U, U)) - alias Unshared = U; - else - alias Unshared = T; -} - -/// -@safe unittest -{ - static assert(is(Unshared!( int) == int)); - static assert(is(Unshared!( const int) == const int)); - static assert(is(Unshared!( inout int) == inout int)); - static assert(is(Unshared!( inout const int) == inout const int)); - static assert(is(Unshared!(shared int) == int)); - static assert(is(Unshared!(shared const int) == const int)); - static assert(is(Unshared!(shared inout int) == inout int)); - static assert(is(Unshared!(shared inout const int) == inout const int)); - static assert(is(Unshared!( immutable int) == immutable int)); - - // Only the outer layer of shared is removed. - // shared(int[]) -> shared(int)[] - alias SharedIntArr = shared(int[]); - static assert(is(Unshared!SharedIntArr == shared(int)[])); - - // Only the outer layer of shared is removed. - // shared(int*) -> shared(int)* - alias SharedIntPtr = shared(int*); - static assert(is(Unshared!SharedIntPtr == shared(int)*)); - - // shared(int)* -> shared(int)* - alias PtrToSharedInt = shared(int)*; - static assert(is(Unshared!PtrToSharedInt == shared(int)*)); - - // immutable is unaffected - alias ImmutableArr = immutable(int[]); - static assert(is(Unshared!ImmutableArr == immutable(int[]))); - - static struct S - { - int* ptr; - } - - shared S s; - static assert(is(typeof(s) == shared S)); - static assert(is(typeof(typeof(s).ptr) == shared int*)); - - // For user-defined types, the shared qualifier is removed entirely. - // shared S -> S - static assert(is(Unshared!(typeof(s)) == S)); - static assert(is(typeof(Unshared!(typeof(s)).ptr) == int*)); - - static struct Foo(T) - { - T* ptr; - } - - // The qualifer on the type is affected, but the qualifier on the template - // argument is not. - static assert(is(Unshared!(shared(Foo!(shared int))) == Foo!(shared int))); - static assert(is(Unshared!(Foo!(shared int)) == Foo!(shared int))); - static assert(is(Unshared!(shared(Foo!int)) == Foo!int)); -} - /++ Whether the given type is one of the built-in integer types, ignoring all qualifiers. @@ -1156,6 +983,179 @@ enum isPointer(T) = is(T == U*, U); } } +/++ + Removes the outer layer of $(D const), $(D inout), or $(D immutable) + from type $(D T). + + If none of those qualifiers have been applied to the outer layer of + type $(D T), then the result is $(D T). + + Due to limitations with D's type system, user-defined types have the type + qualifier removed entirely if present. The types of the member variables + themselves are unaffected beyond how removing the type qualifier from the + type containing them affects them (e.g. an $(D int*) member that is + $(D const(int*)) because the type containing it is $(D const) becomes + $(D int*) when Unconst is used on the containing type, because $(D const) + is removed from the containing type. The member does not become + $(D const(int)*) as would occur if Unconst were used directly on a + $(D const(int*))). + + Also, Unconst has no effect on what a templated type is instantiated with, + so if a templated type is instantiated with a template argument which is a + const type, the template instantiation will not change. + +/ +version (StdDdoc) template Unconst(T) +{ + import core.internal.traits : CoreUnconst = Unconst; + alias Unconst = CoreUnconst!T; +} +else +{ + import core.internal.traits : CoreUnconst = Unconst; + alias Unconst = CoreUnconst; +} + +/// +@safe unittest +{ + static assert(is(Unconst!( int) == int)); + static assert(is(Unconst!( const int) == int)); + static assert(is(Unconst!( inout int) == int)); + static assert(is(Unconst!( inout const int) == int)); + static assert(is(Unconst!(shared int) == shared int)); + static assert(is(Unconst!(shared const int) == shared int)); + static assert(is(Unconst!(shared inout int) == shared int)); + static assert(is(Unconst!(shared inout const int) == shared int)); + static assert(is(Unconst!( immutable int) == int)); + + // Only the outer layer of immutable is removed. + // immutable(int[]) -> immutable(int)[] + alias ImmIntArr = immutable(int[]); + static assert(is(Unconst!ImmIntArr == immutable(int)[])); + + // Only the outer layer of const is removed. + // immutable(int*) -> immutable(int)* + alias ConstIntPtr = const(int*); + static assert(is(Unconst!ConstIntPtr == const(int)*)); + + // const(int)* -> const(int)* + alias PtrToConstInt = const(int)*; + static assert(is(Unconst!PtrToConstInt == const(int)*)); + + static struct S + { + int* ptr; + } + + const S s; + static assert(is(typeof(s) == const S)); + static assert(is(typeof(typeof(s).ptr) == const int*)); + + // For user-defined types, the const qualifier is removed entirely. + // const S -> S + static assert(is(Unconst!(typeof(s)) == S)); + static assert(is(typeof(Unconst!(typeof(s)).ptr) == int*)); + + static struct Foo(T) + { + T* ptr; + } + + // The qualifer on the type is removed, but the qualifier on the template + // argument is not. + static assert(is(Unconst!(const(Foo!(const int))) == Foo!(const int))); + static assert(is(Unconst!(Foo!(const int)) == Foo!(const int))); + static assert(is(Unconst!(const(Foo!int)) == Foo!int)); +} + +/++ + Removes the outer layer of $(D shared) from type $(D T). + + If $(D shared) has not been applied to the outer layer of type $(D T), then + the result is $(D T). + + Note that while $(D immutable) is implicitly $(D shared), it is unaffected + by Unshared. Only explict $(D shared) is removed. + + Due to limitations with D's type system, user-defined types have the type + qualifier removed entirely if present. The types of the member variables + themselves are unaffected beyond how removing the type qualifier from the + type containing them affects them (e.g. an $(D int*) member that is + $(D shared(int*)) because the type containing it is $(D shared) becomes + $(D int*) when Unshared is used on the containing type, because $(D shared) + is removed from the containing type. The member does not become + $(D shared(int)*) as would occur if Unshared were used directly on a + $(D shared(int*))). + + Also, Unshared has no effect on what a templated type is instantiated with, + so if a templated type is instantiated with a template argument which is a + shared type, the template instantiation will not change. + +/ +template Unshared(T) +{ + static if (is(T == shared U, U)) + alias Unshared = U; + else + alias Unshared = T; +} + +/// +@safe unittest +{ + static assert(is(Unshared!( int) == int)); + static assert(is(Unshared!( const int) == const int)); + static assert(is(Unshared!( inout int) == inout int)); + static assert(is(Unshared!( inout const int) == inout const int)); + static assert(is(Unshared!(shared int) == int)); + static assert(is(Unshared!(shared const int) == const int)); + static assert(is(Unshared!(shared inout int) == inout int)); + static assert(is(Unshared!(shared inout const int) == inout const int)); + static assert(is(Unshared!( immutable int) == immutable int)); + + // Only the outer layer of shared is removed. + // shared(int[]) -> shared(int)[] + alias SharedIntArr = shared(int[]); + static assert(is(Unshared!SharedIntArr == shared(int)[])); + + // Only the outer layer of shared is removed. + // shared(int*) -> shared(int)* + alias SharedIntPtr = shared(int*); + static assert(is(Unshared!SharedIntPtr == shared(int)*)); + + // shared(int)* -> shared(int)* + alias PtrToSharedInt = shared(int)*; + static assert(is(Unshared!PtrToSharedInt == shared(int)*)); + + // immutable is unaffected + alias ImmutableArr = immutable(int[]); + static assert(is(Unshared!ImmutableArr == immutable(int[]))); + + static struct S + { + int* ptr; + } + + shared S s; + static assert(is(typeof(s) == shared S)); + static assert(is(typeof(typeof(s).ptr) == shared int*)); + + // For user-defined types, the shared qualifier is removed entirely. + // shared S -> S + static assert(is(Unshared!(typeof(s)) == S)); + static assert(is(typeof(Unshared!(typeof(s)).ptr) == int*)); + + static struct Foo(T) + { + T* ptr; + } + + // The qualifer on the type is affected, but the qualifier on the template + // argument is not. + static assert(is(Unshared!(shared(Foo!(shared int))) == Foo!(shared int))); + static assert(is(Unshared!(Foo!(shared int)) == Foo!(shared int))); + static assert(is(Unshared!(shared(Foo!int)) == Foo!int)); +} + /++ Removes the outer layer of all type qualifiers from type $(D T). From e695747422ffb2b232e6dc30b4d537b7ce6811b5 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 13 Mar 2024 11:19:06 +0000 Subject: [PATCH 17/57] Update cheat sheet description --- std/algorithm/searching.d | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 99f318ca7e0..02dd6450295 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -22,8 +22,9 @@ $(T2 boyerMooreFinder, $(T2 canFind, `canFind("hello world", "or")` returns `true`.) $(T2 count, - Counts elements that are equal to a specified value or satisfy a - predicate. `count([1, 2, 1], 1)` returns `2` and + Counts all elements or elements matching a predicate, specific element or sub-range.$(BR) + `count([1, 2, 1])` returns `3`, + `count([1, 2, 1], 1)` returns `2` and `count!"a < 0"([1, -3, 0])` returns `1`.) $(T2 countUntil, `countUntil(a, b)` returns the number of steps taken in `a` to From 4ea7dd3e07443dd04c55491135b11ff7b46613aa Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 13 Mar 2024 11:22:58 +0000 Subject: [PATCH 18/57] Add short descriptions for each overload set; minor tweaks Short descriptions improve ddox output. Use overload instead of version. --- std/algorithm/searching.d | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 02dd6450295..195e943085d 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -606,15 +606,17 @@ if (isNarrowString!R1 && isNarrowString!R2) // count /** -The first version counts each element `e` in `haystack` for +Counts matches of `needle` in `haystack`. + +The first overload counts each element `e` in `haystack` for which `pred(e, needle)` is `true`. `pred` defaults to equality. Performs $(BIGOH haystack.length) evaluations of `pred`. -The second version counts the number of times `needle` was matched in +The second overload counts the number of times `needle` was matched in `haystack`. `pred` compares elements in each range. -Throws an exception if `needle.empty`, as the _count +Throws an exception if `needle.empty` is `true`, as the _count of the empty range in any range would be infinite. Overlapped counts -are not considered, for example `count("aaa", "aa")` is `1`, not +are *not* considered, for example `count("aaa", "aa")` is `1`, not `2`. Note: Regardless of the overload, `count` will not accept @@ -707,10 +709,12 @@ if (isForwardRange!R1 && !isInfinite!R1 && } /** -The first version counts each element `e` in `haystack` for which `pred(e)` is $(D +Counts all elements or elements satisfying a predicate in `haystack`. + +The first overload counts each element `e` in `haystack` for which `pred(e)` is $(D true). Performs $(BIGOH haystack.length) evaluations of `pred`. -The second version counts the number of elements in a range. +The second overload counts the number of elements in a range. If the given range has the `length` property, that is returned right away, otherwise performs $(BIGOH haystack.length) to walk the range. From 0b45fc77c6f172feadb913267daf4279ff3024a0 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 13 Mar 2024 11:23:16 +0000 Subject: [PATCH 19/57] Fix pred description when there's no needle --- 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 195e943085d..2d89dea3f1c 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -720,7 +720,7 @@ that is returned right away, otherwise performs $(BIGOH haystack.length) to walk the range. Params: - pred = Optional predicate to compare elements. + pred = Optional predicate to find elements. haystack = The range to _count. Returns: From 03c368158ee0e84b0f4a8d4ac6a16289f4121fd4 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sat, 16 Mar 2024 17:07:50 +0000 Subject: [PATCH 20/57] [std.socket] Fix cast assign to getaddrinfoPointer See https://github.com/dlang/dmd/pull/16315 With that pull, using a type qualifier cast is not @safe to use as an lvalue. --- std/socket.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/socket.d b/std/socket.d index 4052479f9ca..b2570f596ca 100644 --- a/std/socket.d +++ b/std/socket.d @@ -1116,7 +1116,7 @@ Address[] getAddress(scope const(char)[] hostname, ushort port) // test via gethostbyname auto getaddrinfoPointerBackup = getaddrinfoPointer; cast() getaddrinfoPointer = null; - scope(exit) cast() getaddrinfoPointer = getaddrinfoPointerBackup; + scope(exit) () @trusted { cast() getaddrinfoPointer = getaddrinfoPointerBackup; }(); addresses = getAddress("63.105.9.61"); assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61"); @@ -1196,7 +1196,7 @@ Address parseAddress(scope const(char)[] hostaddr, ushort port) // test via inet_addr auto getaddrinfoPointerBackup = getaddrinfoPointer; cast() getaddrinfoPointer = null; - scope(exit) cast() getaddrinfoPointer = getaddrinfoPointerBackup; + scope(exit) () @trusted { cast() getaddrinfoPointer = getaddrinfoPointerBackup; }(); address = parseAddress("63.105.9.61"); assert(address.toAddrString() == "63.105.9.61"); @@ -1698,7 +1698,7 @@ public: // test reverse lookup, via gethostbyaddr auto getnameinfoPointerBackup = getnameinfoPointer; cast() getnameinfoPointer = null; - scope(exit) cast() getnameinfoPointer = getnameinfoPointerBackup; + scope(exit) () @trusted { cast() getnameinfoPointer = getnameinfoPointerBackup; }(); assert(ia.toHostNameString() == "digitalmars.com"); } @@ -1716,7 +1716,7 @@ public: // test failing reverse lookup, via gethostbyaddr auto getnameinfoPointerBackup = getnameinfoPointer; cast() getnameinfoPointer = null; - scope(exit) cast() getnameinfoPointer = getnameinfoPointerBackup; + scope(exit) () @trusted { cast() getnameinfoPointer = getnameinfoPointerBackup; }(); assert(ia.toHostNameString() is null); } From 1591ce9fe7d284598d01ab84b7d232848684eb8d Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 18 Mar 2024 09:24:00 -0700 Subject: [PATCH 21/57] swap-and-destroy called too eagerly --- std/typecons.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/typecons.d b/std/typecons.d index 5fac1c9cca4..5c7a3d5ba19 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -971,7 +971,7 @@ if (distinctFieldNames!(Specs)) { import std.algorithm.mutation : swap; - static if (is(R : Tuple!Types) && !__traits(isRef, rhs) && isTuple!R) + static if (is(R == Tuple!Types) && !__traits(isRef, rhs) && isTuple!R) { if (__ctfe) { From 3d909ad863bb9cf8e091ce3b5e2caf4d59944ed0 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 15 Mar 2024 14:04:56 -0600 Subject: [PATCH 22/57] Rename lib.sys.traits' Unqual to Unqualified. The reason for this is because Unqual currently gets used heavily when in many (most?) cases, Unconst should be used instead, because the code in question is not supposed to be operating on shared, and thus stripping off shared will either allow shared types in when they shouldn't be (e.g. with a template constraint), or in the case of a cast, it will actually strip off shared and the protections that it provides, risking concurrency bugs. But a lot of D programmers are in the habit of using Unqual rather than Unconst (probably in part because Unqual has been around longer) when they simply want to remove const, and by renaming it, we will hopefully discourage its use - or at least make programmers think about it briefly rather than unthinkingly grabbing Unqual. Obviously, there are cases where Unqualified should still be used, but hopefully, with the new name, it will be used less when it shouldn't be. I've also adjusted the documentation on Unconst, Unqualified, and Unshared to try to better clarify the situation with user-defined types and member variables (since as Paul Backus has pointed out, it's often incorrectly assumed that Unqual is guaranteed to strip const off of all of the member variables when that's not actually true). --- lib/sys/meta.d | 8 +- lib/sys/traits.d | 240 ++++++++++++++++++++++++++++++----------------- 2 files changed, 158 insertions(+), 90 deletions(-) diff --git a/lib/sys/meta.d b/lib/sys/meta.d index 29543c29178..c05c77efcd0 100644 --- a/lib/sys/meta.d +++ b/lib/sys/meta.d @@ -215,18 +215,18 @@ template Map(alias fun, args...) /// @safe unittest { - import lib.sys.traits : Unqual; + import lib.sys.traits : Unqualified; // empty - alias Empty = Map!Unqual; + alias Empty = Map!Unqualified; static assert(Empty.length == 0); // single - alias Single = Map!(Unqual, const int); + alias Single = Map!(Unqualified, const int); static assert(is(Single == AliasSeq!int)); // several - alias Several = Map!(Unqual, int, const int, immutable int, uint, + alias Several = Map!(Unqualified, int, const int, immutable int, uint, ubyte, byte, short, ushort, const long); static assert(is(Several == AliasSeq!(int, int, int, uint, ubyte, byte, short, ushort, long))); diff --git a/lib/sys/traits.d b/lib/sys/traits.d index 3768ea040af..1e8286fe4fc 100644 --- a/lib/sys/traits.d +++ b/lib/sys/traits.d @@ -62,7 +62,7 @@ $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) $(LREF Unshared) - $(LREF Unqual) + $(LREF Unqualified) )) $(TR $(TD Type Constructors) $(TD $(LREF ConstOf) @@ -990,19 +990,31 @@ enum isPointer(T) = is(T == U*, U); If none of those qualifiers have been applied to the outer layer of type $(D T), then the result is $(D T). - Due to limitations with D's type system, user-defined types have the type - qualifier removed entirely if present. The types of the member variables - themselves are unaffected beyond how removing the type qualifier from the - type containing them affects them (e.g. an $(D int*) member that is - $(D const(int*)) because the type containing it is $(D const) becomes - $(D int*) when Unconst is used on the containing type, because $(D const) - is removed from the containing type. The member does not become - $(D const(int)*) as would occur if Unconst were used directly on a - $(D const(int*))). - - Also, Unconst has no effect on what a templated type is instantiated with, - so if a templated type is instantiated with a template argument which is a - const type, the template instantiation will not change. + For the built-in, scalar types (that is $(D bool), the character types, and + the numeric types), they only have one layer, so $(D const U) simply becomes + $(D U). + + Where the layers come in is pointers and arrays. $(D const(U*)) becomes + $(D const(U)*), and $(D const(U[])), becomes $(D const(U)[]). So, a pointer + goes from being fully $(D const) to being a mutable pointer to $(D const), + and a dynamic array goes from being fully $(D const) to being a mutable + dynamic array of $(D const) elements. And if there are multiple layers of + pointers or arrays, it's just that outer layer which is affected - e.g. + $(D const(U**)) would become $(D const(U*)*). + + For user-defined types, the effect is that $(D const U) becomes $(D U), and + how that affects member variables depends on the type of the member + variable. If a member variable is explicitly marked with any mutability + qualifiers, then it will continue to have those qualifiers even after + Unconst has stripped all mutability qualifiers from the containing type. + However, if a mutability qualifier was on the member variable only because + the containing type had that qualifier, then when Unconst removes the + qualifier from the containing type, it is removed from the member variable + as well. + + Also, Unconst has no effect on what a templated type is instantiated + with, so if a templated type is instantiated with a template argument which + has a mutability qualifier, the template instantiation will not change. +/ version (StdDdoc) template Unconst(T) { @@ -1045,16 +1057,25 @@ else static struct S { int* ptr; + const int* cPtr; + shared int* sPtr; } const S s; static assert(is(typeof(s) == const S)); static assert(is(typeof(typeof(s).ptr) == const int*)); + static assert(is(typeof(typeof(s).cPtr) == const int*)); + static assert(is(typeof(typeof(s).sPtr) == const shared int*)); + + // For user-defined types, all mutability qualifiers that are applied to + // member variables only because the containing type has them are removed, + // but the ones that are directly on those member variables remain. - // For user-defined types, the const qualifier is removed entirely. // const S -> S static assert(is(Unconst!(typeof(s)) == S)); static assert(is(typeof(Unconst!(typeof(s)).ptr) == int*)); + static assert(is(typeof(Unconst!(typeof(s)).cPtr) == const int*)); + static assert(is(typeof(Unconst!(typeof(s)).sPtr) == shared int*)); static struct Foo(T) { @@ -1075,21 +1096,32 @@ else the result is $(D T). Note that while $(D immutable) is implicitly $(D shared), it is unaffected - by Unshared. Only explict $(D shared) is removed. - - Due to limitations with D's type system, user-defined types have the type - qualifier removed entirely if present. The types of the member variables - themselves are unaffected beyond how removing the type qualifier from the - type containing them affects them (e.g. an $(D int*) member that is - $(D shared(int*)) because the type containing it is $(D shared) becomes - $(D int*) when Unshared is used on the containing type, because $(D shared) - is removed from the containing type. The member does not become - $(D shared(int)*) as would occur if Unshared were used directly on a - $(D shared(int*))). - - Also, Unshared has no effect on what a templated type is instantiated with, - so if a templated type is instantiated with a template argument which is a - shared type, the template instantiation will not change. + by Unshared. Only explicit $(D shared) is removed. + + For the built-in, scalar types (that is $(D bool), the character types, and + the numeric types), they only have one layer, so $(D shared U) simply + becomes $(D U). + + Where the layers come in is pointers and arrays. $(D shared(U*)) becomes + $(D shared(U)*), and $(D shared(U[])), becomes $(D shared(U)[]). So, a + pointer goes from being fully $(D shared) to being a mutable pointer to + $(D shared), and a dynamic array goes from being fully $(D shared) to being + a mutable dynamic array of $(D shared) elements. And if there are multiple + layers of pointers or arrays, it's just that outer layer which is affected + - e.g. $(D shared(U**)) would become $(D shared(U*)*). + + For user-defined types, the effect is that $(D shared U) becomes $(D U), + and how that affects member variables depends on the type of the member + variable. If a member variable is explicitly marked with $(D shared), then + it will continue to be $(D shared) even after Unshared has stripped + $(D shared) from the containing type. However, if $(D shared) was on the + member variable only because the containing type was $(D shared), then when + Unshared removes the qualifier from the containing type, it is removed from + the member variable as well. + + Also, Unshared has no effect on what a templated type is instantiated + with, so if a templated type is instantiated with a template argument which + has a type qualifier, the template instantiation will not change. +/ template Unshared(T) { @@ -1133,23 +1165,33 @@ template Unshared(T) static struct S { int* ptr; + const int* cPtr; + shared int* sPtr; } shared S s; static assert(is(typeof(s) == shared S)); static assert(is(typeof(typeof(s).ptr) == shared int*)); + static assert(is(typeof(typeof(s).cPtr) == const shared int*)); + static assert(is(typeof(typeof(s).sPtr) == shared int*)); + + // For user-defined types, if shared is applied to a member variable only + // because the containing type is shared, then shared is removed from that + // member variable, but if the member variable is directly marked as shared, + // then it continues to be shared. - // For user-defined types, the shared qualifier is removed entirely. // shared S -> S static assert(is(Unshared!(typeof(s)) == S)); static assert(is(typeof(Unshared!(typeof(s)).ptr) == int*)); + static assert(is(typeof(Unshared!(typeof(s)).cPtr) == const int*)); + static assert(is(typeof(Unshared!(typeof(s)).sPtr) == shared int*)); static struct Foo(T) { T* ptr; } - // The qualifer on the type is affected, but the qualifier on the template + // The qualifer on the type is removed, but the qualifier on the template // argument is not. static assert(is(Unshared!(shared(Foo!(shared int))) == Foo!(shared int))); static assert(is(Unshared!(Foo!(shared int)) == Foo!(shared int))); @@ -1157,117 +1199,143 @@ template Unshared(T) } /++ - Removes the outer layer of all type qualifiers from type $(D T). + Removes the outer layer of all type qualifiers from type $(D T) - this + includes $(D shared). If no type qualifiers have been applied to the outer layer of type $(D T), then the result is $(D T). - Due to limitations with D's type system, user-defined types have the type - qualifier removed entirely if present. The types of the member variables - themselves are unaffected beyond how removing the type qualifier from the - type containing them affects them (e.g. a $(D int*) member that is - $(D const(int*)) because the type containing it is $(D const) becomes - $(D int*) when Unqual is used on the containing type, because $(D const) - is removed from the containing type. The member does not become - $(D const(int)*) as would occur if Unqual were used directly on a - $(D const(int*))). - - Also, Unqual has no effect on what a templated type is instantiated with, - so if a templated type is instantiated with a template argument which has a - type qualifier, the template instantiation will not change. + For the built-in, scalar types (that is $(D bool), the character types, and + the numeric types), they only have one layer, so $(D const U) simply becomes + $(D U). + + Where the layers come in is pointers and arrays. $(D const(U*)) becomes + $(D const(U)*), and $(D const(U[])), becomes $(D const(U)[]). So, a pointer + goes from being fully $(D const) to being a mutable pointer to $(D const), + and a dynamic array goes from being fully $(D const) to being a mutable + dynamic array of $(D const) elements. And if there are multiple layers of + pointers or arrays, it's just that outer layer which is affected - e.g. + $(D shared(U**)) would become $(D shared(U*)*). + + For user-defined types, the effect is that $(D const U) becomes $(D U), and + how that affects member variables depends on the type of the member + variable. If a member variable is explicitly marked with any qualifiers, + then it will continue to have those qualifiers even after Unqualified has + stripped all qualifiers from the containing type. However, if a qualifier + was on the member variable only because the containing type had that + qualifier, then when Unqualified removes the qualifier from the containing + type, it is removed from the member variable as well. + + Also, Unqualified has no effect on what a templated type is instantiated + with, so if a templated type is instantiated with a template argument which + has a type qualifier, the template instantiation will not change. Note that in most cases, $(LREF Unconst) or $(LREF Unshared) should be used - rather than Unqual, because in most cases, code is not designed to work with - $(D shared) and thus doing type checks which remove $(D shared) will allow - $(D shared) types to pass template constraints when they won't actually - work with the code. And when code is designed to work with $(D shared), - it's often the case that the type checks need to take $(D const) into - account to work properly. - - In particular, historically, a lot of D code has used Unqual when the - programmer's intent was to remove $(D const), and $(D shared) wasn't - actually considered at all. And in such cases, the code really should use - $(LREF Unconst) instead. + rather than Unqualified, because in most cases, code is not designed to + work with $(D shared) and thus doing type checks which remove $(D shared) + will allow $(D shared) types to pass template constraints when they won't + actually work with the code. And when code is designed to work with + $(D shared), it's often the case that the type checks need to take + $(D const) into account in order to avoid accidentally mutating $(D const) + data and violating the type system. + + In particular, historically, a lot of D code has used + $(REF Unqual, std, traits) (which is equivalent to lib.sys.traits' + Unqualified) when the programmer's intent was to remove $(D const), and + $(D shared) wasn't actually considered at all. And in such cases, the code + really should use $(LREF Unconst) instead. But of course, if a template constraint or $(D static if) really needs to strip off both the mutability qualifers and $(D shared) for what it's - testing for, then that's what Unqual is for. + testing for, then that's what Unqualified is for. It's just that it's best + practice to use $(LREF Unconst) when it's not clear that $(D shared) should + be removed as well. +/ -version (StdDdoc) template Unqual(T) +version (StdDdoc) template Unqualified(T) { - import core.internal.traits : CoreUnqual = Unqual; - alias Unqual = CoreUnqual!(T); + import core.internal.traits : CoreUnqualified = Unqual; + alias Unqualified = CoreUnqualified!(T); } else { - import core.internal.traits : CoreUnqual = Unqual; - alias Unqual = CoreUnqual; + import core.internal.traits : CoreUnqualified = Unqual; + alias Unqualified = CoreUnqualified; } +/// @safe unittest { - static assert(is(Unqual!( int) == int)); - static assert(is(Unqual!( const int) == int)); - static assert(is(Unqual!( inout int) == int)); - static assert(is(Unqual!( inout const int) == int)); - static assert(is(Unqual!(shared int) == int)); - static assert(is(Unqual!(shared const int) == int)); - static assert(is(Unqual!(shared inout int) == int)); - static assert(is(Unqual!(shared inout const int) == int)); - static assert(is(Unqual!( immutable int) == int)); + static assert(is(Unqualified!( int) == int)); + static assert(is(Unqualified!( const int) == int)); + static assert(is(Unqualified!( inout int) == int)); + static assert(is(Unqualified!( inout const int) == int)); + static assert(is(Unqualified!(shared int) == int)); + static assert(is(Unqualified!(shared const int) == int)); + static assert(is(Unqualified!(shared inout int) == int)); + static assert(is(Unqualified!(shared inout const int) == int)); + static assert(is(Unqualified!( immutable int) == int)); // Only the outer layer of immutable is removed. // immutable(int[]) -> immutable(int)[] alias ImmIntArr = immutable(int[]); - static assert(is(Unqual!ImmIntArr == immutable(int)[])); + static assert(is(Unqualified!ImmIntArr == immutable(int)[])); // Only the outer layer of const is removed. // const(int*) -> const(int)* alias ConstIntPtr = const(int*); - static assert(is(Unqual!ConstIntPtr == const(int)*)); + static assert(is(Unqualified!ConstIntPtr == const(int)*)); // const(int)* -> const(int)* alias PtrToConstInt = const(int)*; - static assert(is(Unqual!PtrToConstInt == const(int)*)); + static assert(is(Unqualified!PtrToConstInt == const(int)*)); // Only the outer layer of shared is removed. // shared(int*) -> shared(int)* alias SharedIntPtr = shared(int*); - static assert(is(Unqual!SharedIntPtr == shared(int)*)); + static assert(is(Unqualified!SharedIntPtr == shared(int)*)); // shared(int)* -> shared(int)* alias PtrToSharedInt = shared(int)*; - static assert(is(Unqual!PtrToSharedInt == shared(int)*)); + static assert(is(Unqualified!PtrToSharedInt == shared(int)*)); // Both const and shared are removed from the outer layer. // shared const int[] -> shared(const(int))[] alias SharedConstIntArr = shared const(int[]); - static assert(is(Unqual!SharedConstIntArr == shared(const(int))[])); + static assert(is(Unqualified!SharedConstIntArr == shared(const(int))[])); static struct S { int* ptr; + const int* cPtr; + shared int* sPtr; } shared const S s; static assert(is(typeof(s) == shared const S)); static assert(is(typeof(typeof(s).ptr) == shared const int*)); + static assert(is(typeof(typeof(s).cPtr) == shared const int*)); + static assert(is(typeof(typeof(s).sPtr) == shared const int*)); + + // For user-defined types, all qualifiers that are applied to member + // variables only because the containing type has them are removed, but the + // ones that are directly on those member variables remain. - // For user-defined types, the qualifiers are removed entirely. // shared const S -> S - static assert(is(Unqual!(typeof(s)) == S)); - static assert(is(typeof(Unqual!(typeof(s)).ptr) == int*)); + static assert(is(Unqualified!(typeof(s)) == S)); + static assert(is(typeof(Unqualified!(typeof(s)).ptr) == int*)); + static assert(is(typeof(Unqualified!(typeof(s)).cPtr) == const int*)); + static assert(is(typeof(Unqualified!(typeof(s)).sPtr) == shared int*)); static struct Foo(T) { T* ptr; } - // The qualifers on the type are affected, but the qualifiers on the - // template argument is not. - static assert(is(Unqual!(const(Foo!(const int))) == Foo!(const int))); - static assert(is(Unqual!(Foo!(const int)) == Foo!(const int))); - static assert(is(Unqual!(const(Foo!int)) == Foo!int)); + // The qualifers on the type are removed, but the qualifiers on the + // template argument are not. + static assert(is(Unqualified!(const(Foo!(const int))) == Foo!(const int))); + static assert(is(Unqualified!(Foo!(const int)) == Foo!(const int))); + static assert(is(Unqualified!(const(Foo!int)) == Foo!int)); } /++ From f6b2932268d8b801e5cf66649f7c047eeebc9366 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Tue, 19 Mar 2024 14:36:26 +0100 Subject: [PATCH 23/57] Remove explicit hex string casts --- std/internal/unicode_comp.d | 24 ++-- std/internal/unicode_decomp.d | 24 ++-- std/internal/unicode_grapheme.d | 84 ++++++------ std/internal/unicode_norm.d | 48 +++---- std/internal/unicode_tables.d | 220 ++++++++++++++++---------------- tools/unicode_table_generator.d | 8 +- 6 files changed, 204 insertions(+), 204 deletions(-) diff --git a/std/internal/unicode_comp.d b/std/internal/unicode_comp.d index 646aeeb3af0..28b2e0d26b9 100644 --- a/std/internal/unicode_comp.d +++ b/std/internal/unicode_comp.d @@ -7,11 +7,11 @@ import std.internal.unicode_tables; static if (size_t.sizeof == 4) { //10144 bytes -enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 8, 5)(cast(immutable size_t[]) x" +enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 8, 5)(x" 0000000000000040000005C0", -cast(immutable size_t[]) x" +x" 0000010000000B00000010A0", -cast(immutable size_t[]) x" +x" 020201000402030206020205090807020000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -226,11 +226,11 @@ E600E6E6E6E600E600E6E6E600000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000", ); enum composeIdxMask = (1 << 11) - 1, composeCntShift = 11; -enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)(cast(immutable size_t[]) x" +enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)(x" 0000000000000800", -cast(immutable size_t[]) x" +x" 0000100000002600", -cast(immutable size_t[]) x" +x" 00010000000300020005000400070006000700080007000700090007000A0007000C000B000700070007000700070007 0007000D0007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 @@ -919,11 +919,11 @@ return t[]; static if (size_t.sizeof == 8) { //10144 bytes -enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 8, 5)(cast(immutable size_t[]) x" +enum combiningClassTrieEntries = TrieEntry!(ubyte, 8, 8, 5)(x" 0000000000000000000000000000002000000000000002E0", -cast(immutable size_t[]) x" +x" 00000000000001000000000000000B0000000000000010A0", -cast(immutable size_t[]) x" +x" 040203020202010009080702060202050000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -1138,11 +1138,11 @@ E6E6E6E600000000000000000007E6E6000000000000000000000000000000000000000000000000 00000000000000000000000000000000", ); enum composeIdxMask = (1 << 11) - 1, composeCntShift = 11; -enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)(cast(immutable size_t[]) x" +enum compositionJumpTrieEntries = TrieEntry!(ushort, 12, 9)(x" 00000000000000000000000000000400", -cast(immutable size_t[]) x" +x" 00000000000010000000000000002600", -cast(immutable size_t[]) x" +x" 000300020001000000070006000500040007000700070008000A00070009000700070007000C000B0007000700070007 000700070007000D00070007000700070007000700070007000700070007000700070007000700070007000700070007 000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007 diff --git a/std/internal/unicode_decomp.d b/std/internal/unicode_decomp.d index 6016f3dee23..d596d48c3ee 100644 --- a/std/internal/unicode_decomp.d +++ b/std/internal/unicode_decomp.d @@ -19,11 +19,11 @@ import std.internal.unicode_tables; static if (size_t.sizeof == 4) { //23488 bytes -enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(cast(immutable size_t[]) x" +enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" 000000000000004000000540", -cast(immutable size_t[]) x" +x" 0000010000000A0000002360", -cast(immutable size_t[]) x" +x" 020201000402030202020205070602020202020208020202000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -516,11 +516,11 @@ cast(immutable size_t[]) x" 00000000000000000000000000000000", ); //12544 bytes -enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(cast(immutable size_t[]) x" +enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" 000000000000004000000440", -cast(immutable size_t[]) x" +x" 000001000000080000001000", -cast(immutable size_t[]) x" +x" 020201000302020202020204020502020202020206020202000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -1636,11 +1636,11 @@ return t[]; static if (size_t.sizeof == 8) { //23488 bytes -enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(cast(immutable size_t[]) x" +enum compatMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" 0000000000000000000000000000002000000000000002A0", -cast(immutable size_t[]) x" +x" 00000000000001000000000000000A000000000000002360", -cast(immutable size_t[]) x" +x" 040203020202010007060202020202050802020202020202000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -2133,11 +2133,11 @@ cast(immutable size_t[]) x" 00000000000000000000000000000000", ); //12544 bytes -enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(cast(immutable size_t[]) x" +enum canonMappingTrieEntries = TrieEntry!(ushort, 8, 8, 5)(x" 000000000000000000000000000000200000000000000220", -cast(immutable size_t[]) x" +x" 000000000000010000000000000008000000000000001000", -cast(immutable size_t[]) x" +x" 030202020202010002050202020202040602020202020202000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/std/internal/unicode_grapheme.d b/std/internal/unicode_grapheme.d index d33e987de2c..ba80e188a80 100644 --- a/std/internal/unicode_grapheme.d +++ b/std/internal/unicode_grapheme.d @@ -19,11 +19,11 @@ package(std): static if (size_t.sizeof == 4) { //832 bytes -enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000080", -cast(immutable size_t[]) x" +x" 000001000000008000000A00", -cast(immutable size_t[]) x" +x" 000000000002010000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -44,11 +44,11 @@ cast(immutable size_t[]) x" 00000000000000000000000000000000", ); //832 bytes -enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000080", -cast(immutable size_t[]) x" +x" 000001000000008000000A00", -cast(immutable size_t[]) x" +x" 000000000002010000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -69,11 +69,11 @@ FEFFFFFFFFEFFFFFFFFEFFFFFFFFEFFFFFFFFEFF0000000F00000000000000000000000000000000 00000000000000000000000000000000", ); //896 bytes -enum prependTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum prependTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000080", -cast(immutable size_t[]) x" +x" 000001000000008000000C00", -cast(immutable size_t[]) x" +x" 010101000101010101010102010101010101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 @@ -95,11 +95,11 @@ cast(immutable size_t[]) x" 0000000000000000000000000000000000000000000000000000000000000000", ); //1280 bytes -enum controlTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum controlTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000D0", -cast(immutable size_t[]) x" +x" 000001000000012000000E00", -cast(immutable size_t[]) x" +x" 020201000302020202020402020605020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020702020202020202020202020202020202020202020000000000000000 @@ -129,11 +129,11 @@ FFFFFFFF000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000 0000000000000000000000000000000000000000000000000000000000000000", ); //1856 bytes -enum spacingMarkTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum spacingMarkTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000B0", -cast(immutable size_t[]) x" +x" 00000100000000E000002400", -cast(immutable size_t[]) x" +x" 010101000101020104010103010501010101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 @@ -175,11 +175,11 @@ cast(immutable size_t[]) x" 0000000000000000000000000000000000000000000000000000000000000000", ); //3488 bytes -enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000110", -cast(immutable size_t[]) x" +x" 00000100000001A000004B00", -cast(immutable size_t[]) x" +x" 0202010004020302070206050A0908020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020B02020202020202020202020202020202020202020000000000000000 @@ -255,11 +255,11 @@ FFFFFFFFF87FFFFFFFFFFFFF00201FFFF80000100000FFFE0000000000000000F9FFFF7F000007DB 0000000000000000000000000000000000000000000000000000000000000000", ); //1344 bytes -enum Extended_PictographicTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum Extended_PictographicTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000090", -cast(immutable size_t[]) x" +x" 00000100000000A000001800", -cast(immutable size_t[]) x" +x" 020201000202020202020202030202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -296,11 +296,11 @@ FFFFFFFFFFFFFFFFFFFFFFFF3FFFFFFF000000000000000000000000000000000000000000000000 static if (size_t.sizeof == 8) { //832 bytes -enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum hangulLVTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000040", -cast(immutable size_t[]) x" +x" 000000000000010000000000000000800000000000000A00", -cast(immutable size_t[]) x" +x" 000201000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -321,11 +321,11 @@ cast(immutable size_t[]) x" 00000000000000000000000000000000", ); //832 bytes -enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum hangulLVTTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000040", -cast(immutable size_t[]) x" +x" 000000000000010000000000000000800000000000000A00", -cast(immutable size_t[]) x" +x" 000201000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -346,11 +346,11 @@ FFEFFFFFFEFFFFFFFFFFEFFFFFFEFFFF0000000FFFFFFEFF00000000000000000000000000000000 00000000000000000000000000000000", ); //896 bytes -enum prependTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum prependTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000040", -cast(immutable size_t[]) x" +x" 000000000000010000000000000000800000000000000C00", -cast(immutable size_t[]) x" +x" 010101010101010001010101010101020101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 @@ -372,11 +372,11 @@ cast(immutable size_t[]) x" 0000000000000000000000000000000000000000000000000000000000000000", ); //1280 bytes -enum controlTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum controlTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000068", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001200000000000000E00", -cast(immutable size_t[]) x" +x" 030202020202010002060502020204020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020207020202020202020202020202020202020000000000000000 @@ -406,11 +406,11 @@ FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000", ); //1856 bytes -enum spacingMarkTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum spacingMarkTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000058", -cast(immutable size_t[]) x" +x" 000000000000010000000000000000E00000000000002400", -cast(immutable size_t[]) x" +x" 010102010101010001050101040101030101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 010101010101010101010101010101010101010101010101010101010101010101010101010101010000000000000000 @@ -452,11 +452,11 @@ C0300000000000080000000000000002000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000", ); //3488 bytes -enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum graphemeExtendTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000088", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001A00000000000004B00", -cast(immutable size_t[]) x" +x" 04020302020201000A090802070206050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 02020202020202020202020202020202020202020202020B020202020202020202020202020202020000000000000000 @@ -532,11 +532,11 @@ F87FFFFFFFFFFFFF00201FFFFFFFFFFF0000FFFEF80000100000000000000000000007DBF9FFFF7F 0000000000000000000000000000000000000000000000000000000000000000", ); //1344 bytes -enum Extended_PictographicTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum Extended_PictographicTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000048", -cast(immutable size_t[]) x" +x" 000000000000010000000000000000A00000000000001800", -cast(immutable size_t[]) x" +x" 020202020202010003020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 diff --git a/std/internal/unicode_norm.d b/std/internal/unicode_norm.d index c103c254d7e..bc51c8cd610 100644 --- a/std/internal/unicode_norm.d +++ b/std/internal/unicode_norm.d @@ -19,11 +19,11 @@ package(std): static if (size_t.sizeof == 4) { //1728 bytes -enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000C0", -cast(immutable size_t[]) x" +x" 000001000000010000001E00", -cast(immutable size_t[]) x" +x" 020201000302020202020204020502020202020206020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -62,11 +62,11 @@ FFFFFFFFFFFFFFFF03FFFFFF00000000A00000005F7FFC0000007FDB000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ); //2048 bytes -enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000E0", -cast(immutable size_t[]) x" +x" 000001000000014000002400", -cast(immutable size_t[]) x" +x" 020201000504030202020206020702020202020208020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -112,11 +112,11 @@ A00000005F7FFC0000007FDB00000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000", ); //2848 bytes -enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000E0", -cast(immutable size_t[]) x" +x" 000001000000014000003D00", -cast(immutable size_t[]) x" +x" 020201000402030202020205070602020202020208020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -179,11 +179,11 @@ FFFF07FFFFFF7FFF0000FFFF00001C0000010000000000000000000000000000FFFF00070FFFFFFF 00000000000000000000000000000000", ); //2944 bytes -enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000F0", -cast(immutable size_t[]) x" +x" 000001000000016000003E00", -cast(immutable size_t[]) x" +x" 020201000504030202020206080702020202020209020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -254,11 +254,11 @@ FFFF07FFFFFF7FFF0000FFFF00001C0000010000000000000000000000000000FFFF00070FFFFFFF static if (size_t.sizeof == 8) { //1728 bytes -enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000060", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001000000000000001E00", -cast(immutable size_t[]) x" +x" 030202020202010002050202020202040602020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -297,11 +297,11 @@ FFFFFFFFFFFFFFFF0000000003FFFFFF5F7FFC00A00000000000000000007FDB0000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ); //2048 bytes -enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000070", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001400000000000002400", -cast(immutable size_t[]) x" +x" 050403020202010002070202020202060802020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -347,11 +347,11 @@ F8000000000000000000000000000001000000003FFFFFFF00000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000", ); //2848 bytes -enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfkcQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000070", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001400000000000003D00", -cast(immutable size_t[]) x" +x" 040203020202010007060202020202050802020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -414,11 +414,11 @@ FFFF7FFFFFFF07FF00001C000000FFFF000000000001000000000000000000000FFFFFFFFFFF0007 00000000000000000000000000000000", ); //2944 bytes -enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum nfkdQCTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000078", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001600000000000003E00", -cast(immutable size_t[]) x" +x" 050403020202010008070202020202060902020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 diff --git a/std/internal/unicode_tables.d b/std/internal/unicode_tables.d index cc0225b1a51..a686e389171 100644 --- a/std/internal/unicode_tables.d +++ b/std/internal/unicode_tables.d @@ -75,7 +75,7 @@ struct TrieEntry(T...) SCE simpleCaseTable(size_t i) { -static immutable uint[] t = cast(immutable uint[]) x" +static immutable uint[] t = x" 0201E90B0211E92D0201E9110211E93302000496021004970200A7220210A72302001F7902101FF902001F4402101F4C 0200015A0210015B020010FD02101CBD02016E4C02116E6C02001E3802101E390201E9210211E94302001F2302101F2B 020001A0021001A1030003A3031003C2032003C3020004DC021004DD02002CA602102CA70200017B0210017C0201E906 @@ -329,7 +329,7 @@ return SCE(t[i]); } @property FCE fullCaseTable(size_t index) nothrow @nogc @safe pure { -static immutable ulong[] t = cast(immutable ulong[]) x" +static immutable ulong[] t = x" 001E90B000000021001E92D0000001210010CAE0000000210010CEE00000012100004960000000210000497000000121 001E911000000021001E933000000121000A722000000021000A7230000001210001F790000000210001FF9000000121 0001F440000000210001F4C000000121000015A000000021000015B00000012100010FD0000000210001CBD000000121 @@ -2285,11 +2285,11 @@ bool isHangT(dchar ch) @safe pure nothrow @nogc static if (size_t.sizeof == 4) { //2080 bytes -enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000E0", -cast(immutable size_t[]) x" +x" 000001000000014000002500", -cast(immutable size_t[]) x" +x" 020201000402030206020205080702020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -2336,11 +2336,11 @@ FFFFFFC0FC000000000FFFFFFFFFC000000000FF0FFFFFFCFFC000000000FFFFFFFFFC000000003F 00000000000000000000000000000000", ); //1856 bytes -enum upperCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum upperCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000000E0", -cast(immutable size_t[]) x" +x" 000001000000014000001E00", -cast(immutable size_t[]) x" +x" 020201000402030206020205080702020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -2382,11 +2382,11 @@ F0000000001FFFFFFFC0000000007FFFFFFF0000000001FF0000040000000000FFFFFFFF00000003 0000000000000000000000000000000000000000000000000000000000000000", ); //11648 bytes -enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 0000010000000480000011C0", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -2632,11 +2632,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //11648 bytes -enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 0000010000000480000011C0", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -2882,11 +2882,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //5600 bytes -enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000001A0", -cast(immutable size_t[]) x" +x" 00000100000002C000007B00", -cast(immutable size_t[]) x" +x" 03020100060504030A0908070E0D0C0B0303030311100F03141413121414141414141414141414141414141414141414 141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414 141414141414141414141414141414141414141414141414141414141414141414141414141414140000000000000000 @@ -3006,11 +3006,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF 0000000000000000000000000000000000000000000000000000000000000000", ); //3392 bytes -enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000110", -cast(immutable size_t[]) x" +x" 00000100000001A000004800", -cast(immutable size_t[]) x" +x" 0202010004020302070206050A0908020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020B02020202020202020202020202020202020202020000000000000000 @@ -3084,11 +3084,11 @@ F9FFFF7F000007DB0000000000000000000080000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000", ); //2848 bytes -enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)(cast(immutable size_t[]) x" +enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)(x" 0000000000000040000001A0", -cast(immutable size_t[]) x" +x" 00000100000002C000002500", -cast(immutable size_t[]) x" +x" 020201000402030207020605090802020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -3151,11 +3151,11 @@ FFFFFFFF001EEFFF0000000000000000FFFFFFFE3FFFBFFF000000000000000000001FFF00000000 00000000000000000000000003FF0000", ); //3360 bytes -enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000100", -cast(immutable size_t[]) x" +x" 000001000000018000004900", -cast(immutable size_t[]) x" +x" 0202010004020302070206050A0908020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -3228,11 +3228,11 @@ E8003600000000000000000000003C000000000000000000001000000000000000003FFF00000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ); //3424 bytes -enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000004000000100", -cast(immutable size_t[]) x" +x" 000001000000018000004B00", -cast(immutable size_t[]) x" +x" 0302010005030403070303060A0908030303030303030303030303030303030303030303030303030303030303030303 030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303 030303030303030303030303030303030303030303030303030303030303030303030303030303030000000000000000 @@ -3307,11 +3307,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFF000007FF000000000000000000000000 00000000000000000000000000000000", ); //6080 bytes -enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000040000001A0", -cast(immutable size_t[]) x" +x" 00000100000002C000008A00", -cast(immutable size_t[]) x" +x" 0202010005040302090807060D0C0B0A02020202100F0E02131312111313131313131313131313131313131313131313 131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313 131313131313131313131313131313131313131413131313131313131313131313131313131313130000000000000000 @@ -3441,11 +3441,11 @@ FFFFFFFF0000FFFF0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 0000000000000000000000000000000000000000000000000000000000000000", ); //4824 bytes -enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)(cast(immutable size_t[]) x" +enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)(x" 00000000000000200000009800000298", -cast(immutable size_t[]) x" +x" 00000080000000F000000400000043C0", -cast(immutable size_t[]) x" +x" 0302010007060504090801010B0B0B0A0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B 0B0B0B0B0B0B0B0B0B0B0B0C0D0101010D01010100000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000010000000300020005000400070006 @@ -3552,11 +3552,11 @@ enum MAX_SIMPLE_LOWER = 1433; enum MAX_SIMPLE_UPPER = 1450; enum MAX_SIMPLE_TITLE = 1454; //10496 bytes -enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 000001000000048000000F80", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -3778,11 +3778,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10112 bytes -enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 000001000000048000000EC0", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -3996,11 +3996,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10496 bytes -enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 000001000000048000000F80", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -4222,11 +4222,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10368 bytes -enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 000001000000048000000F40", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -4445,11 +4445,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //9856 bytes -enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 000001000000048000000E40", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -4658,11 +4658,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10368 bytes -enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000004000000280", -cast(immutable size_t[]) x" +x" 000001000000048000000F40", -cast(immutable size_t[]) x" +x" 020201000402030206020205070202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -4882,7 +4882,7 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ); immutable(uint)[] toUpperTable() nothrow @nogc pure @safe { static immutable uint[] t = -cast(immutable uint[]) x" +x" 0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C 0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 @@ -5027,7 +5027,7 @@ return t; } immutable(uint)[] toLowerTable() nothrow @nogc pure @safe { static immutable uint[] t = -cast(immutable uint[]) x" +x" 0000006100000062000000630000006400000065000000660000006700000068000000690000006A0000006B0000006C 0000006D0000006E0000006F000000700000007100000072000000730000007400000075000000760000007700000078 000000790000007A000000E0000000E1000000E2000000E3000000E4000000E5000000E6000000E7000000E8000000E9 @@ -5161,7 +5161,7 @@ return t; } immutable(uint)[] toTitleTable() nothrow @nogc pure @safe { static immutable uint[] t = -cast(immutable uint[]) x" +x" 0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C 0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 @@ -5307,11 +5307,11 @@ return t; static if (size_t.sizeof == 8) { //2080 bytes -enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum lowerCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000070", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001400000000000002500", -cast(immutable size_t[]) x" +x" 040203020202010008070202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -5358,11 +5358,11 @@ FFFFFFFC00000000000000000000000F000000000000000000000000000000000000000000000000 00000000000000000000000000000000", ); //1856 bytes -enum upperCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum upperCaseTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000070", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001400000000000001E00", -cast(immutable size_t[]) x" +x" 040203020202010008070202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -5404,11 +5404,11 @@ FFF0000003FFFFFFFFFFFF0000003FFF003FDE64D0000003000003FFFFFF00007B0000001FDFE7B0 0000000000000000000000000000000000000000000000000000000000000000", ); //11648 bytes -enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum simpleCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 0000000000000100000000000000048000000000000011C0", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -5654,11 +5654,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //11648 bytes -enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum fullCaseTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 0000000000000100000000000000048000000000000011C0", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -5904,11 +5904,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //5600 bytes -enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum alphaTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000000000000000000002000000000000000D0", -cast(immutable size_t[]) x" +x" 000000000000010000000000000002C00000000000007B00", -cast(immutable size_t[]) x" +x" 06050403030201000E0D0C0B0A09080711100F0303030303141414141414131214141414141414141414141414141414 141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414 141414141414141414141414141414141414141414141414141414141414141414141414141414140000000000000000 @@ -6028,11 +6028,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFF 0000000000000000000000000000000000000000000000000000000000000000", ); //3392 bytes -enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum markTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000088", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001A00000000000004800", -cast(immutable size_t[]) x" +x" 04020302020201000A090802070206050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 02020202020202020202020202020202020202020202020B020202020202020202020202020202020000000000000000 @@ -6106,11 +6106,11 @@ B47E00000000000000000000000000BF0000000000FB7C0000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000", ); //2848 bytes -enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)(cast(immutable size_t[]) x" +enum numberTrieEntries = TrieEntry!(bool, 8, 6, 7)(x" 0000000000000000000000000000002000000000000000D0", -cast(immutable size_t[]) x" +x" 000000000000010000000000000002C00000000000002500", -cast(immutable size_t[]) x" +x" 040203020202010009080202070206050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -6173,11 +6173,11 @@ FFFE0000000003FF0000000000000000000003FF000000000000000000000000003F000000000000 000000000000000003FF000000000000", ); //3360 bytes -enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum punctuationTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000080", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001800000000000004900", -cast(immutable size_t[]) x" +x" 04020302020201000A090802070206050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020000000000000000 @@ -6250,11 +6250,11 @@ FE000000000000000000000000000000000000001E00000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ); //3424 bytes -enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum symbolTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 000000000000000000000000000000200000000000000080", -cast(immutable size_t[]) x" +x" 000000000000010000000000000001800000000000004B00", -cast(immutable size_t[]) x" +x" 05030403030201000A090803070303060303030303030303030303030303030303030303030303030303030303030303 030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303 030303030303030303030303030303030303030303030303030303030303030303030303030303030000000000000000 @@ -6329,11 +6329,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFF00000000000007FF0000000000000000 00000000000000000000000000000000", ); //6080 bytes -enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)(cast(immutable size_t[]) x" +enum graphicalTrieEntries = TrieEntry!(bool, 8, 5, 8)(x" 0000000000000000000000000000002000000000000000D0", -cast(immutable size_t[]) x" +x" 000000000000010000000000000002C00000000000008A00", -cast(immutable size_t[]) x" +x" 05040302020201000D0C0B0A09080706100F0E0202020202131313131313121113131313131313131313131313131313 131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313 131313131313131313131313131313131313131313131314131313131313131313131313131313130000000000000000 @@ -6463,11 +6463,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 0000000000000000000000000000000000000000000000000000000000000000", ); //4824 bytes -enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)(cast(immutable size_t[]) x" +enum nonCharacterTrieEntries = TrieEntry!(bool, 7, 4, 4, 6)(x" 00000000000000000000000000000010000000000000004C000000000000014C", -cast(immutable size_t[]) x" +x" 000000000000008000000000000000F0000000000000040000000000000043C0", -cast(immutable size_t[]) x" +x" 07060504030201000B0B0B0A090801010B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B 0B0B0B0B0B0B0B0B0D0101010B0B0B0C000000000D010101000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000030002000100000007000600050004 @@ -6574,11 +6574,11 @@ enum MAX_SIMPLE_LOWER = 1433; enum MAX_SIMPLE_UPPER = 1450; enum MAX_SIMPLE_TITLE = 1454; //10496 bytes -enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toUpperIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 000000000000010000000000000004800000000000000F80", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -6800,11 +6800,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10112 bytes -enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toLowerIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 000000000000010000000000000004800000000000000EC0", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -7018,11 +7018,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10496 bytes -enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toTitleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 000000000000010000000000000004800000000000000F80", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -7244,11 +7244,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10368 bytes -enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toUpperSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 000000000000010000000000000004800000000000000F40", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -7467,11 +7467,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //9856 bytes -enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toLowerSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 000000000000010000000000000004800000000000000E40", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -7680,11 +7680,11 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ); //10368 bytes -enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(cast(immutable size_t[]) x" +enum toTitleSimpleIndexTrieEntries = TrieEntry!(ushort, 8, 7, 6)(x" 000000000000000000000000000000200000000000000140", -cast(immutable size_t[]) x" +x" 000000000000010000000000000004800000000000000F40", -cast(immutable size_t[]) x" +x" 040203020202010007020202060202050202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202 @@ -7904,7 +7904,7 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ); immutable(uint)[] toUpperTable() nothrow @nogc pure @safe { static immutable uint[] t = -cast(immutable uint[]) x" +x" 0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C 0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 @@ -8049,7 +8049,7 @@ return t; } immutable(uint)[] toLowerTable() nothrow @nogc pure @safe { static immutable uint[] t = -cast(immutable uint[]) x" +x" 0000006100000062000000630000006400000065000000660000006700000068000000690000006A0000006B0000006C 0000006D0000006E0000006F000000700000007100000072000000730000007400000075000000760000007700000078 000000790000007A000000E0000000E1000000E2000000E3000000E4000000E5000000E6000000E7000000E8000000E9 @@ -8183,7 +8183,7 @@ return t; } immutable(uint)[] toTitleTable() nothrow @nogc pure @safe { static immutable uint[] t = -cast(immutable uint[]) x" +x" 0000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C 0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058 000000590000005A0000039C000000C0000000C1000000C2000000C3000000C4000000C5000000C6000000C7000000C8 diff --git a/tools/unicode_table_generator.d b/tools/unicode_table_generator.d index fd74589c388..0bbe80f608d 100644 --- a/tools/unicode_table_generator.d +++ b/tools/unicode_table_generator.d @@ -898,7 +898,7 @@ void writeCaseFolding(File sink) writeln("SCE simpleCaseTable(size_t i)"); writeln("{"); - writef("static immutable uint[] t = cast(immutable uint[]) x\""); + writef("static immutable uint[] t = x\""); foreach (i, v; simpleTable) { if (i % 12 == 0) writeln(); @@ -915,7 +915,7 @@ void writeCaseFolding(File sink) writeln("}"); writeln("@property FCE fullCaseTable(size_t index) nothrow @nogc @safe pure"); writeln("{"); - writef("static immutable ulong[] t = cast(immutable ulong[]) x\""); + writef("static immutable ulong[] t = x\""); int[4] maxS = 0; foreach (i, v; fullTable) { @@ -1162,7 +1162,7 @@ void writeFunctions(File sink) void writeUintArray(T:dchar)(File sink, const T[] tab) { size_t lineCount = 1; - sink.write("cast(immutable uint[]) x\""); + sink.write("x\""); foreach (i, elem; tab) { if ((i % 12) == 0) @@ -1461,7 +1461,7 @@ void storeTrie(T, O)(T trie, O sink) import std.format.write : formattedWrite; void store(size_t[] arr) { - formattedWrite(sink, "cast(immutable size_t[]) x\""); + formattedWrite(sink, "x\""); foreach (i; 0 .. arr.length) { static if (size_t.sizeof == 8) From 90af8c22220028689275d62434fa42114ad6487f Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 20 Mar 2024 16:12:06 -0600 Subject: [PATCH 24/57] Rename lib to phobos. (#8953) As discussed in a recent planning meeting, we're renaming lib to phobos, making it so that the root for Phobos v3 is phobos. So, that's what this commit does. However, on top of that, I removed the existing package.d files (both lib/package.d and lib/sys/package.d) rather than fixing them, since I'm inclined to think that we should be very selective about when we use package.d, and I think that allowing something like import phobos; is a mistake. It wouldn't surprise me if we get screaming about that later, and maybe we'll add it back at that point, but I'd rather not worry about maintaining it as modules get added, and if we can get away with it, I think that we should just not have it at all, since it's better practice in general to limit the symbols that you import to something close to what you're actually using, whereas import phobos; would be importing everything. And having a package.d at each level like lib/sys/package.d seems to be trying to do would complicate things even further. We may very well end up with some package.d files in some cases, but I think that that should be handled on a case-by-case basis - and that the default should be to only have one module to import a symbol from. So, with these changes, lib becomes phobos, and there are no longer any package.d files in it. --- dub.sdl | 4 +-- lib/package.d | 3 --- lib/sys/package.d | 4 --- {lib => phobos}/sys/compiler.d | 4 +-- {lib => phobos}/sys/meta.d | 6 ++--- {lib => phobos}/sys/system.d | 4 +-- {lib => phobos}/sys/traits.d | 46 +++++++++++++++++----------------- 7 files changed, 32 insertions(+), 39 deletions(-) delete mode 100644 lib/package.d delete mode 100644 lib/sys/package.d rename {lib => phobos}/sys/compiler.d (96%) rename {lib => phobos}/sys/meta.d (98%) rename {lib => phobos}/sys/system.d (98%) rename {lib => phobos}/sys/traits.d (97%) diff --git a/dub.sdl b/dub.sdl index 721781a544f..516e5968293 100644 --- a/dub.sdl +++ b/dub.sdl @@ -6,7 +6,7 @@ copyright "Copyright © 1999-2024, The D Language Foundation" configuration "library" { targetType "staticLibrary" - sourcePaths "lib" + sourcePaths "phobos" targetPath "generated-lib" #excludedSourceFiles "unittest.d" "test/**" "std/**" "tools/**" "etc/**" } @@ -14,7 +14,7 @@ configuration "library" { configuration "unittest" { dflags "-main" targetType "executable" - sourcePaths "lib" + sourcePaths "phobos" targetPath "generated-lib" #excludedSourceFiles "unittest.d" "test/**" "std/**" "tools/**" "etc/**" } diff --git a/lib/package.d b/lib/package.d deleted file mode 100644 index cff4c95fe77..00000000000 --- a/lib/package.d +++ /dev/null @@ -1,3 +0,0 @@ -module lib; - -public import lib.sys; diff --git a/lib/sys/package.d b/lib/sys/package.d deleted file mode 100644 index 04c8903b17c..00000000000 --- a/lib/sys/package.d +++ /dev/null @@ -1,4 +0,0 @@ -module lib.sys; - -public import lib.sys.compiler; -public import lib.sys.system; diff --git a/lib/sys/compiler.d b/phobos/sys/compiler.d similarity index 96% rename from lib/sys/compiler.d rename to phobos/sys/compiler.d index 88bf362de0a..8181c9e3f10 100644 --- a/lib/sys/compiler.d +++ b/phobos/sys/compiler.d @@ -6,14 +6,14 @@ * Copyright: Copyright The D Language Foundation 2000 - 2011. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: $(HTTP digitalmars.com, Walter Bright), Alex Rønne Petersen - * Source: $(PHOBOSSRC std/compiler.d) + * Source: $(PHOBOSSRC phobos/sys/compiler.d) */ /* Copyright The D Language Foundation 2000 - 2011. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ -module lib.sys.compiler; +module phobos.sys.compiler; immutable { diff --git a/lib/sys/meta.d b/phobos/sys/meta.d similarity index 98% rename from lib/sys/meta.d rename to phobos/sys/meta.d index c05c77efcd0..c57b7714cc3 100644 --- a/lib/sys/meta.d +++ b/phobos/sys/meta.d @@ -69,9 +69,9 @@ Authors: $(HTTP digitalmars.com, Walter Bright), $(HTTP klickverbot.at, David Nadlinger) $(HTTP jmdavisprog.com, Jonathan M Davis) - Source: $(PHOBOSSRC lib/sys/meta) + Source: $(PHOBOSSRC phobos/sys/meta) +/ -module lib.sys.meta; +module phobos.sys.meta; // Example for converting types to values from module documentation. @safe unittest @@ -215,7 +215,7 @@ template Map(alias fun, args...) /// @safe unittest { - import lib.sys.traits : Unqualified; + import phobos.sys.traits : Unqualified; // empty alias Empty = Map!Unqualified; diff --git a/lib/sys/system.d b/phobos/sys/system.d similarity index 98% rename from lib/sys/system.d rename to phobos/sys/system.d index 7e5a15cfe89..538db6d70c8 100644 --- a/lib/sys/system.d +++ b/phobos/sys/system.d @@ -7,9 +7,9 @@ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: $(HTTP digitalmars.com, Walter Bright) and $(HTTP jmdavisprog.com, Jonathan M Davis) - * Source: $(PHOBOSSRC std/system.d) + * Source: $(PHOBOSSRC phobos/sys/system.d) */ -module lib.sys.system; +module phobos.sys.system; immutable { diff --git a/lib/sys/traits.d b/phobos/sys/traits.d similarity index 97% rename from lib/sys/traits.d rename to phobos/sys/traits.d index 1e8286fe4fc..70c79d5cd0d 100644 --- a/lib/sys/traits.d +++ b/phobos/sys/traits.d @@ -2,7 +2,7 @@ /++ Templates which extract information about types and symbols at compile time. - In the context of lib.sys.traits, a "trait" is a template which provides + In the context of phobos.sys.traits, a "trait" is a template which provides information about a type or symbol. Most traits evaluate to $(D true) or $(D false), telling the code using it whether the given arguments match / have that specific trait (e.g. whether the given type is @@ -83,9 +83,9 @@ $(HTTP klickverbot.at, David Nadlinger), Kenji Hara, Shoichi Kato - Source: $(PHOBOSSRC lib/sys/traits) + Source: $(PHOBOSSRC phobos/sys/traits) +/ -module lib.sys.traits; +module phobos.sys.traits; /++ Whether the given type is a dynamic array (or what is sometimes referred to @@ -242,7 +242,7 @@ enum isDynamicArray(T) = is(T == U[], U); @safe unittest { - import lib.sys.meta : Alias, AliasSeq; + import phobos.sys.meta : Alias, AliasSeq; static struct AliasThis(T) { @@ -376,7 +376,7 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); @safe unittest { - import lib.sys.meta : Alias, AliasSeq; + import phobos.sys.meta : Alias, AliasSeq; static struct AliasThis(T) { @@ -516,7 +516,7 @@ enum isInteger(T) = is(immutable T == immutable byte) || @safe unittest { - import lib.sys.meta : Alias, AliasSeq; + import phobos.sys.meta : Alias, AliasSeq; static struct AliasThis(T) { @@ -661,7 +661,7 @@ enum isFloatingPoint(T) = is(immutable T == immutable float) || @safe unittest { - import lib.sys.meta : Alias, AliasSeq; + import phobos.sys.meta : Alias, AliasSeq; static struct AliasThis(T) { @@ -820,7 +820,7 @@ enum isNumeric(T) = is(immutable T == immutable byte) || @safe unittest { - import lib.sys.meta : Alias, AliasSeq; + import phobos.sys.meta : Alias, AliasSeq; static struct AliasThis(T) { @@ -947,7 +947,7 @@ enum isPointer(T) = is(T == U*, U); @safe unittest { - import lib.sys.meta : Alias, AliasSeq; + import phobos.sys.meta : Alias, AliasSeq; static struct AliasThis(T) { @@ -1240,7 +1240,7 @@ template Unshared(T) data and violating the type system. In particular, historically, a lot of D code has used - $(REF Unqual, std, traits) (which is equivalent to lib.sys.traits' + $(REF Unqual, std, traits) (which is equivalent to phobos.sys.traits' Unqualified) when the programmer's intent was to remove $(D const), and $(D shared) wasn't actually considered at all. And in such cases, the code really should use $(LREF Unconst) instead. @@ -1342,9 +1342,9 @@ else Applies $(D const) to the given type. This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in lib.sys.meta), since while in + predicate (such as many of the templates in phobos.sys.meta), since while in most cases, you can simply do $(D const T) or $(D const(T)) to make $(D T) - $(D const), with something like $(REF Map, lib, sys, meta), you need to + $(D const), with something like $(REF Map, phobos, sys, meta), you need to pass a template to be applied. See_Also: @@ -1365,7 +1365,7 @@ alias ConstOf(T) = const T; // Note that const has no effect on immutable. static assert(is(ConstOf!(immutable int) == immutable int)); - import lib.sys.meta : AliasSeq, Map; + import phobos.sys.meta : AliasSeq, Map; alias Types = AliasSeq!(int, long, bool*, ubyte[], @@ -1381,10 +1381,10 @@ alias ConstOf(T) = const T; Applies $(D immutable) to the given type. This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in lib.sys.meta), since while in + predicate (such as many of the templates in phobos.sys.meta), since while in most cases, you can simply do $(D immutable T) or $(D immutable(T)) to make - $(D T) $(D immutable), with something like $(REF Map, lib, sys, meta), you - need to pass a template to be applied. + $(D T) $(D immutable), with something like $(REF Map, phobos, sys, meta), + you need to pass a template to be applied. See_Also: $(LREF ConstOf) @@ -1408,7 +1408,7 @@ alias ImmutableOf(T) = immutable T; static assert(is(ImmutableOf!(immutable int) == immutable int)); - import lib.sys.meta : AliasSeq, Map; + import phobos.sys.meta : AliasSeq, Map; alias Types = AliasSeq!(int, long, bool*, ubyte[], @@ -1424,9 +1424,9 @@ alias ImmutableOf(T) = immutable T; Applies $(D inout) to the given type. This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in lib.sys.meta), since while in + predicate (such as many of the templates in phobos.sys.meta), since while in most cases, you can simply do $(D inout T) or $(D inout(T)) to make $(D T) - $(D inout), with something like $(REF Map, lib, sys, meta), you need to + $(D inout), with something like $(REF Map, phobos, sys, meta), you need to pass a template to be applied. See_Also: @@ -1447,7 +1447,7 @@ alias InoutOf(T) = inout T; // Note that inout has no effect on immutable. static assert(is(InoutOf!(immutable int) == immutable int)); - import lib.sys.meta : AliasSeq, Map; + import phobos.sys.meta : AliasSeq, Map; alias Types = AliasSeq!(int, long, bool*, ubyte[], @@ -1463,9 +1463,9 @@ alias InoutOf(T) = inout T; Applies $(D shared) to the given type. This is primarily useful in conjunction with templates that take a template - predicate (such as many of the templates in lib.sys.meta), since while in + predicate (such as many of the templates in phobos.sys.meta), since while in most cases, you can simply do $(D shared T) or $(D shared(T)) to make $(D T) - $(D shared), with something like $(REF Map, lib, sys, meta), you need to + $(D shared), with something like $(REF Map, phobos, sys, meta), you need to pass a template to be applied. See_Also: @@ -1487,7 +1487,7 @@ alias SharedOf(T) = shared T; // implicitly shared. static assert(is(SharedOf!(immutable int) == immutable int)); - import lib.sys.meta : AliasSeq, Map; + import phobos.sys.meta : AliasSeq, Map; alias Types = AliasSeq!(int, long, bool*, ubyte[], From 3919c7f31a193b5436611e22b7ea3c02344bf9bd Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 21 Mar 2024 13:36:59 -0600 Subject: [PATCH 25/57] Add isSignedInteger and isUnsignedInteger to phobos.sys.traits. (#8954) They're similar to isSigned and isUnsigned from std.traits, but these new traits are far more restrictive, because they're only true for the four signed/unsigned built-in integer types, whereas isSigned and isUnsigned accept a bunch of different types (including floating point types and types that implicitly convert to real in the case of isSigned). From what I've looked at in Phobos v2, most uses of isSigned and isUnsigned are used specifically when the code already knows that it's operating on integer types anyway, so these traits should work for most of those cases, and floating point types are always signed, so including them in a check for signedness seems kind of redundant. I suppose that isNumericSigned might make sense under some set of circumstances, since it would allow for a single template instantiation instead of two like you'd get with isSignedInteger!T || isFloatingPoint!T, but at least for now, I think that isSignedInteger and isUnsignedInteger are enough. --- phobos/sys/traits.d | 322 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 314 insertions(+), 8 deletions(-) diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 70c79d5cd0d..7f66d3e6edf 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -57,7 +57,9 @@ $(LREF isInteger) $(LREF isNumeric) $(LREF isPointer) + $(LREF isSignedInteger) $(LREF isStaticArray) + $(LREF isUnsignedInteger) )) $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) @@ -113,7 +115,7 @@ module phobos.sys.traits; determined by how the dynamic array was created in the first place) - though if you do any operations on it which end up requiring allocation (e.g. appending to it if it doesn't have the capacity to expand in-place, - which it won't if it isn't a slice of GC-allocated memory), then that + which it won't if it isn't a slice of GC-allocated memory), then that reallocation will result in the dynamic array being a slice of newly allocated, GC-backed memory (regardless of what it was a slice of before), since it's the GC that deals with those allocations. @@ -427,8 +429,8 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); Note that this does not include implicit conversions or enum types. The type itself must be one of the built-in integer types. - This trait does have some similarities to $(D __traits(isIntegral, T)), but - $(D isIntegral) accepts a $(D lot) more types than isInteger does. + This trait does have some similarities with $(D __traits(isIntegral, T)), + but $(D isIntegral) accepts a $(D lot) more types than isInteger does. isInteger is specifically for testing for the built-in integer types, whereas $(D isIntegral) tests for a whole set of types that are vaguely integer-like (including $(D bool), the three built-in character types, and @@ -439,7 +441,9 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); See also: $(DDSUBLINK spec/traits, isIntegral, $(D __traits(isIntegral, T))) $(LREF isFloatingPoint) + $(LREF isSignedInteger) $(LREF isNumeric) + $(LREF isUnsignedInteger) +/ enum isInteger(T) = is(immutable T == immutable byte) || is(immutable T == immutable ubyte) || @@ -566,6 +570,304 @@ enum isInteger(T) = is(immutable T == immutable byte) || } } +/++ + Whether the given type is one of the built-in signed integer types, ignoring + all qualifiers. + + $(TABLE + $(TR $(TH Signed Integer Types)) + $(TR $(TD byte)) + $(TR $(TD short)) + $(TR $(TD int)) + $(TR $(TD long)) + ) + + Note that this does not include implicit conversions or enum types. The + type itself must be one of the built-in signed integer types. + + See also: + $(LREF isFloatingPoint) + $(LREF isInteger) + $(LREF isNumeric) + $(LREF isUnsignedInteger) + +/ +enum isSignedInteger(T) = is(immutable T == immutable byte) || + is(immutable T == immutable short) || + is(immutable T == immutable int) || + is(immutable T == immutable long); + +/// +@safe unittest +{ + // Some types which are signed integer types. + static assert( isSignedInteger!byte); + static assert( isSignedInteger!short); + static assert( isSignedInteger!int); + static assert( isSignedInteger!long); + + static assert( isSignedInteger!(const byte)); + static assert( isSignedInteger!(immutable short)); + static assert( isSignedInteger!(inout int)); + static assert( isSignedInteger!(shared int)); + static assert( isSignedInteger!(const shared long)); + + static assert( isSignedInteger!(typeof(42))); + static assert( isSignedInteger!(typeof(1234567890L))); + + int i; + static assert( isSignedInteger!(typeof(i))); + + // Some types which aren't signed integer types. + static assert(!isSignedInteger!ubyte); + static assert(!isSignedInteger!ushort); + static assert(!isSignedInteger!uint); + static assert(!isSignedInteger!ulong); + + static assert(!isSignedInteger!bool); + static assert(!isSignedInteger!char); + static assert(!isSignedInteger!wchar); + static assert(!isSignedInteger!dchar); + static assert(!isSignedInteger!(int[])); + static assert(!isSignedInteger!(ubyte[4])); + static assert(!isSignedInteger!(int*)); + static assert(!isSignedInteger!double); + static assert(!isSignedInteger!string); + + static struct S + { + int i; + } + static assert(!isSignedInteger!S); + + // The struct itself isn't considered a signed integer, + // but its member variable is when checked directly. + static assert( isSignedInteger!(typeof(S.i))); + + enum E : int + { + a = 42 + } + + // Enums do not count. + static assert(!isSignedInteger!E); + + static struct AliasThis + { + int i; + alias this = i; + } + + // Other implicit conversions do not count. + static assert(!isSignedInteger!AliasThis); +} + +@safe unittest +{ + import phobos.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + // The actual core.simd types available vary from system to system, so we + // have to be a bit creative here. The reason that we're testing these types + // is because __traits(isIntegral, T) accepts them, but isSignedInteger is + // not supposed to. + template SIMDTypes() + { + import core.simd; + + alias SIMDTypes = AliasSeq!(); + static if (is(ubyte16)) + SIMDTypes = AliasSeq!(SIMDTypes, ubyte16); + static if (is(int4)) + SIMDTypes = AliasSeq!(SIMDTypes, int4); + static if (is(double2)) + SIMDTypes = AliasSeq!(SIMDTypes, double2); + static if (is(void16)) + SIMDTypes = AliasSeq!(SIMDTypes, void16); + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(byte, short, int, long)) + { + enum E : Q!T { a = Q!T.init } + + static assert( isSignedInteger!(Q!T)); + static assert(!isSignedInteger!E); + static assert(!isSignedInteger!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, + bool, char, wchar, dchar, float, double, real, SIMDTypes!(), + int[], ubyte[8], dchar[], void[], long*)) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isSignedInteger!(Q!T)); + static assert(!isSignedInteger!E); + static assert(!isSignedInteger!(AliasThis!(Q!T))); + } + } +} + +/++ + Whether the given type is one of the built-in unsigned integer types, + ignoring all qualifiers. + + $(TABLE + $(TR $(TH Integer Types)) + $(TR $(TD ubyte)) + $(TR $(TD ushort)) + $(TR $(TD uint)) + $(TR $(TD ulong)) + ) + + Note that this does not include implicit conversions or enum types. The + type itself must be one of the built-in unsigned integer types. + + This trait does have some similarities with $(D __traits(isUnsigned, T)), + but $(D isUnsigned) accepts a $(D lot) more types than isUnsignedInteger + does. isUnsignedInteger is specifically for testing for the built-in + unsigned integer types, whereas $(D isUnsigned) tests for a whole set of + types that are unsigned and vaguely integer-like (including $(D bool), the + three built-in character types, and some of the vector types from + core.simd). So, for most code, isUnsignedInteger is going to be more + appropriate, but obviously, it depends on what the code is trying to do. + + See also: + $(DDSUBLINK spec/traits, isUnsigned, $(D __traits(isUnsigned, T))) + $(LREF isFloatingPoint) + $(LREF isInteger) + $(LREF isSignedInteger) + $(LREF isNumeric) + +/ +enum isUnsignedInteger(T) = is(immutable T == immutable ubyte) || + is(immutable T == immutable ushort) || + is(immutable T == immutable uint) || + is(immutable T == immutable ulong); + +/// +@safe unittest +{ + // Some types which are unsigned integer types. + static assert( isUnsignedInteger!ubyte); + static assert( isUnsignedInteger!ushort); + static assert( isUnsignedInteger!uint); + static assert( isUnsignedInteger!ulong); + + static assert( isUnsignedInteger!(const ubyte)); + static assert( isUnsignedInteger!(immutable ushort)); + static assert( isUnsignedInteger!(inout uint)); + static assert( isUnsignedInteger!(shared uint)); + static assert( isUnsignedInteger!(const shared ulong)); + + static assert( isUnsignedInteger!(typeof(42u))); + static assert( isUnsignedInteger!(typeof(1234567890UL))); + + uint u; + static assert( isUnsignedInteger!(typeof(u))); + + // Some types which aren't unsigned integer types. + static assert(!isUnsignedInteger!byte); + static assert(!isUnsignedInteger!short); + static assert(!isUnsignedInteger!int); + static assert(!isUnsignedInteger!long); + + static assert(!isUnsignedInteger!bool); + static assert(!isUnsignedInteger!char); + static assert(!isUnsignedInteger!wchar); + static assert(!isUnsignedInteger!dchar); + static assert(!isUnsignedInteger!(int[])); + static assert(!isUnsignedInteger!(ubyte[4])); + static assert(!isUnsignedInteger!(int*)); + static assert(!isUnsignedInteger!double); + static assert(!isUnsignedInteger!string); + + static struct S + { + uint u; + } + static assert(!isUnsignedInteger!S); + + // The struct itself isn't considered an unsigned integer, + // but its member variable is when checked directly. + static assert( isUnsignedInteger!(typeof(S.u))); + + enum E : uint + { + a = 42 + } + + // Enums do not count. + static assert(!isUnsignedInteger!E); + + static struct AliasThis + { + uint u; + alias this = u; + } + + // Other implicit conversions do not count. + static assert(!isUnsignedInteger!AliasThis); +} + +@safe unittest +{ + import phobos.sys.meta : Alias, AliasSeq; + + static struct AliasThis(T) + { + T member; + alias this = member; + } + + // The actual core.simd types available vary from system to system, so we + // have to be a bit creative here. The reason that we're testing these types + // is because __traits(isIntegral, T) and __traits(isUnsigned, T) accept + // them, but isUnsignedInteger is not supposed to. + template SIMDTypes() + { + import core.simd; + + alias SIMDTypes = AliasSeq!(); + static if (is(ubyte16)) + SIMDTypes = AliasSeq!(SIMDTypes, ubyte16); + static if (is(int4)) + SIMDTypes = AliasSeq!(SIMDTypes, int4); + static if (is(double2)) + SIMDTypes = AliasSeq!(SIMDTypes, double2); + static if (is(void16)) + SIMDTypes = AliasSeq!(SIMDTypes, void16); + } + + foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) + { + foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) + { + enum E : Q!T { a = Q!T.init } + + static assert( isUnsignedInteger!(Q!T)); + static assert(!isUnsignedInteger!E); + static assert(!isUnsignedInteger!(AliasThis!(Q!T))); + } + + foreach (T; AliasSeq!(byte, short, int, long, + bool, char, wchar, dchar, float, double, real, SIMDTypes!(), + int[], ubyte[8], dchar[], void[], long*)) + { + enum E : Q!T { a = Q!T.init } + + static assert(!isUnsignedInteger!(Q!T)); + static assert(!isUnsignedInteger!E); + static assert(!isUnsignedInteger!(AliasThis!(Q!T))); + } + } +} + /++ Whether the given type is one of the built-in floating-point types, ignoring all qualifiers. @@ -580,8 +882,8 @@ enum isInteger(T) = is(immutable T == immutable byte) || Note that this does not include implicit conversions or enum types. The type itself must be one of the built-in floating-point types. - This trait does have some similarities to $(D __traits(isFloating, T)), but - $(D isFloating) accepts more types than isFloatingPoint does. + This trait does have some similarities with $(D __traits(isFloating, T)), + but $(D isFloating) accepts more types than isFloatingPoint does. isFloatingPoint is specifically for testing for the built-in floating-point types, whereas $(D isFloating) tests for a whole set of types that are vaguely float-like (including enums with a base type which is a @@ -592,7 +894,9 @@ enum isInteger(T) = is(immutable T == immutable byte) || See also: $(DDSUBLINK spec/traits, isFloating, $(D __traits(isFloating, T))) $(LREF isInteger) + $(LREF isSignedInteger) $(LREF isNumeric) + $(LREF isUnsignedInteger) +/ enum isFloatingPoint(T) = is(immutable T == immutable float) || is(immutable T == immutable double) || @@ -736,6 +1040,8 @@ enum isFloatingPoint(T) = is(immutable T == immutable float) || See_Also: $(LREF isFloatingPoint) $(LREF isInteger) + $(LREF isSignedInteger) + $(LREF isUnsignedInteger) +/ enum isNumeric(T) = is(immutable T == immutable byte) || is(immutable T == immutable ubyte) || @@ -990,7 +1296,7 @@ enum isPointer(T) = is(T == U*, U); If none of those qualifiers have been applied to the outer layer of type $(D T), then the result is $(D T). - For the built-in, scalar types (that is $(D bool), the character types, and + For the built-in scalar types (that is $(D bool), the character types, and the numeric types), they only have one layer, so $(D const U) simply becomes $(D U). @@ -1098,7 +1404,7 @@ else Note that while $(D immutable) is implicitly $(D shared), it is unaffected by Unshared. Only explicit $(D shared) is removed. - For the built-in, scalar types (that is $(D bool), the character types, and + For the built-in scalar types (that is $(D bool), the character types, and the numeric types), they only have one layer, so $(D shared U) simply becomes $(D U). @@ -1205,7 +1511,7 @@ template Unshared(T) If no type qualifiers have been applied to the outer layer of type $(D T), then the result is $(D T). - For the built-in, scalar types (that is $(D bool), the character types, and + For the built-in scalar types (that is $(D bool), the character types, and the numeric types), they only have one layer, so $(D const U) simply becomes $(D U). From b2853536e940a66c723681e15656f2fa77b517c0 Mon Sep 17 00:00:00 2001 From: Horodniceanu Andrei Date: Fri, 22 Mar 2024 09:03:46 +0200 Subject: [PATCH 26/57] std/file.d: Don't check for OS-dependent error message in unittest Signed-off-by: Horodniceanu Andrei --- std/file.d | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/std/file.d b/std/file.d index 5b8925d5eb4..1db779bdf8e 100644 --- a/std/file.d +++ b/std/file.d @@ -1083,6 +1083,7 @@ private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @t @safe unittest { import std.exception : collectExceptionMsg, assertThrown; + import std.algorithm.searching : startsWith; string filename = null; // e.g. as returned by File.tmpfile.name @@ -1090,12 +1091,10 @@ private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @t { // exact exception message is OS-dependent auto msg = filename.remove.collectExceptionMsg!FileException; - assert("Failed to remove file (null): Bad address" == msg, msg); + assert(msg.startsWith("Failed to remove file (null):"), msg); } else version (Windows) { - import std.algorithm.searching : startsWith; - // don't test exact message on windows, it's language dependent auto msg = filename.remove.collectExceptionMsg!FileException; assert(msg.startsWith("(null):"), msg); From 2fc0991cf9c3c1c0bea9563dcdad3526312b32b0 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 22 Mar 2024 15:52:17 -0600 Subject: [PATCH 27/57] Add isImplicitlyConvertible and isQualifierConvertible to phobos v3. (#8955) Both traits do exactly the same thing as their std.traits counterparts, though these have far more documentation and examples. I'm not a big fan of isImplicitlyConvertible, since it's usually a bad idea to test for implicit conversions in template constraints, and the is expression is incredibly simple to boot, but a trait _is_ needed when a template predicate is needed (e.g. for the templates in phobos.sys.meta), and having isImplicitlyConvertible allows for a good place to put documentation on implicit conversions in general (as well as to warn against testing for them in template constraints). isQualifierConvertible isn't actually used much in Phobos v2 right now (in most cases right now, Unqual is used with an is expression instead), but it is used by the range traits, and there probably are a number of cases where it would be better to use it than Unqualified or Unconst with an is expression. But it's new enough that I don't think that many of us have really thought through how best to use it yet. Either way, if we want to be able to do something like isInputRange!(R, const char), then it's a good trait to have. --- phobos/sys/traits.d | 427 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 420 insertions(+), 7 deletions(-) diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 7f66d3e6edf..24788bb5ff9 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -61,6 +61,10 @@ $(LREF isStaticArray) $(LREF isUnsignedInteger) )) + $(TR $(TD Traits testing for type conversions) $(TD + $(LREF isImplicitlyConvertible) + $(LREF isQualifierConvertible) + )) $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) $(LREF Unshared) @@ -294,7 +298,7 @@ enum isDynamicArray(T) = is(T == U[], U); $(DDSUBLINK spec/traits, isStaticArray, $(D __traits(isStaticArray, T))) $(DDSUBLINK spec/arrays, , The language spec for arrays) +/ -enum bool isStaticArray(T) = is(T == U[n], U, size_t n); +enum isStaticArray(T) = is(T == U[n], U, size_t n); /// @safe unittest @@ -430,7 +434,7 @@ enum bool isStaticArray(T) = is(T == U[n], U, size_t n); type itself must be one of the built-in integer types. This trait does have some similarities with $(D __traits(isIntegral, T)), - but $(D isIntegral) accepts a $(D lot) more types than isInteger does. + but $(D isIntegral) accepts a $(I lot) more types than isInteger does. isInteger is specifically for testing for the built-in integer types, whereas $(D isIntegral) tests for a whole set of types that are vaguely integer-like (including $(D bool), the three built-in character types, and @@ -730,7 +734,7 @@ enum isSignedInteger(T) = is(immutable T == immutable byte) || type itself must be one of the built-in unsigned integer types. This trait does have some similarities with $(D __traits(isUnsigned, T)), - but $(D isUnsigned) accepts a $(D lot) more types than isUnsignedInteger + but $(D isUnsigned) accepts a $(I lot) more types than isUnsignedInteger does. isUnsignedInteger is specifically for testing for the built-in unsigned integer types, whereas $(D isUnsigned) tests for a whole set of types that are unsigned and vaguely integer-like (including $(D bool), the @@ -1289,6 +1293,415 @@ enum isPointer(T) = is(T == U*, U); } } +/++ + Whether the type $(D From) is implicitly convertible to the type $(D To). + + Note that template constraints should be very careful about when they test + for implicit conversions and in general should prefer to either test for an + exact set of types or for types which compile with a particular piece of + code rather than being designed to accept any type which implicitly converts + to a particular type. + + This is because having a type pass a template constraint based on an + implicit conversion but then not have the implicit conversion actually take + place (which it won't unless the template does something to force it + internally) can lead to either compilation errors or subtle behavioral + differences - and even when the conversion is done explicitly within a + templated function, since it's not done at the call site, it can still lead + to subtle bugs in some cases (e.g. if slicing a static array is involved). + + For situations where code needs to verify that a type is implicitly + convertible based solely on its qualifiers, $(LREF isQualifierConvertible) + would be a more appropriate choice than isImplicitlyConvertible. + + Given how trivial the $(D is) expression for isImplicitlyConvertible is - + $(D is(To : From)) - this trait is provided primarily so that it can be + used in conjunction with templates that use a template predicate (such as + many of the templates in phobos.sys.meta). + + See_Also: + $(DDSUBLINK dlang.org/spec/type, implicit-conversions, Spec on implicit conversions) + $(DDSUBLINK spec/const3, implicit_qualifier_conversions, Spec for implicit qualifier conversions) + $(LREF isQualifierConvertible) + +/ +enum isImplicitlyConvertible(From, To) = is(From : To); + +/// +@safe unittest +{ + static assert( isImplicitlyConvertible!(byte, long)); + static assert( isImplicitlyConvertible!(ushort, long)); + static assert( isImplicitlyConvertible!(int, long)); + static assert( isImplicitlyConvertible!(long, long)); + static assert( isImplicitlyConvertible!(ulong, long)); + + static assert( isImplicitlyConvertible!(ubyte, int)); + static assert( isImplicitlyConvertible!(short, int)); + static assert( isImplicitlyConvertible!(int, int)); + static assert( isImplicitlyConvertible!(uint, int)); + static assert(!isImplicitlyConvertible!(long, int)); + static assert(!isImplicitlyConvertible!(ulong, int)); + + static assert(!isImplicitlyConvertible!(int, string)); + static assert(!isImplicitlyConvertible!(int, int[])); + static assert(!isImplicitlyConvertible!(int, int*)); + + static assert(!isImplicitlyConvertible!(string, int)); + static assert(!isImplicitlyConvertible!(int[], int)); + static assert(!isImplicitlyConvertible!(int*, int)); + + // For better or worse, bool and the built-in character types will + // implicitly convert to integer or floating-point types if the target type + // is large enough. Sometimes, this is desirable, whereas at other times, + // it can have very surprising results, so it's one reason why code should + // be very careful when testing for implicit conversions. + static assert( isImplicitlyConvertible!(bool, int)); + static assert( isImplicitlyConvertible!(char, int)); + static assert( isImplicitlyConvertible!(wchar, int)); + static assert( isImplicitlyConvertible!(dchar, int)); + + static assert( isImplicitlyConvertible!(bool, ubyte)); + static assert( isImplicitlyConvertible!(char, ubyte)); + static assert(!isImplicitlyConvertible!(wchar, ubyte)); + static assert(!isImplicitlyConvertible!(dchar, ubyte)); + + static assert( isImplicitlyConvertible!(bool, double)); + static assert( isImplicitlyConvertible!(char, double)); + static assert( isImplicitlyConvertible!(wchar, double)); + static assert( isImplicitlyConvertible!(dchar, double)); + + // Value types can be implicitly converted regardless of their qualifiers + // thanks to the fact that they're copied. + static assert( isImplicitlyConvertible!(int, int)); + static assert( isImplicitlyConvertible!(const int, int)); + static assert( isImplicitlyConvertible!(immutable int, int)); + static assert( isImplicitlyConvertible!(inout int, int)); + + static assert( isImplicitlyConvertible!(int, const int)); + static assert( isImplicitlyConvertible!(int, immutable int)); + static assert( isImplicitlyConvertible!(int, inout int)); + + // Reference types are far more restrictive about which implicit conversions + // they allow, because qualifiers in D are transitive. + static assert( isImplicitlyConvertible!(int*, int*)); + static assert(!isImplicitlyConvertible!(const int*, int*)); + static assert(!isImplicitlyConvertible!(immutable int*, int*)); + + static assert( isImplicitlyConvertible!(int*, const int*)); + static assert( isImplicitlyConvertible!(const int*, const int*)); + static assert( isImplicitlyConvertible!(immutable int*, const int*)); + + static assert(!isImplicitlyConvertible!(int*, immutable int*)); + static assert(!isImplicitlyConvertible!(const int*, immutable int*)); + static assert( isImplicitlyConvertible!(immutable int*, immutable int*)); + + // Note that inout gets a bit weird, since it's only used with function + // parameters, and it's a stand-in for whatever mutability qualifiers the + // type actually has. So, a function parameter that's inout accepts any + // mutability, but you can't actually implicitly convert to inout, because + // it's unknown within the function what the actual mutability of the type + // is. It will differ depending on the function arguments of a specific + // call to that function, so the same code has to work with all combinations + // of mutability qualifiers. + static assert(!isImplicitlyConvertible!(int*, inout int*)); + static assert(!isImplicitlyConvertible!(const int*, inout int*)); + static assert(!isImplicitlyConvertible!(immutable int*, inout int*)); + static assert( isImplicitlyConvertible!(inout int*, inout int*)); + + static assert(!isImplicitlyConvertible!(inout int*, int*)); + static assert( isImplicitlyConvertible!(inout int*, const int*)); + static assert(!isImplicitlyConvertible!(inout int*, immutable int*)); + + // Enums implicitly convert to their base type. + enum E : int + { + a = 42 + } + static assert( isImplicitlyConvertible!(E, int)); + static assert( isImplicitlyConvertible!(E, long)); + static assert(!isImplicitlyConvertible!(E, int[])); + + // Structs only implicit convert to another type via declaring an + // alias this. + static struct S + { + int i; + } + static assert(!isImplicitlyConvertible!(S, int)); + static assert(!isImplicitlyConvertible!(S, long)); + static assert(!isImplicitlyConvertible!(S, string)); + + static struct AliasThis + { + int i; + alias this = i; + } + static assert( isImplicitlyConvertible!(AliasThis, int)); + static assert( isImplicitlyConvertible!(AliasThis, long)); + static assert(!isImplicitlyConvertible!(AliasThis, string)); + + static struct AliasThis2 + { + AliasThis at; + alias this = at; + } + static assert( isImplicitlyConvertible!(AliasThis2, AliasThis)); + static assert( isImplicitlyConvertible!(AliasThis2, int)); + static assert( isImplicitlyConvertible!(AliasThis2, long)); + static assert(!isImplicitlyConvertible!(AliasThis2, string)); + + static struct AliasThis3 + { + AliasThis2 at; + alias this = at; + } + static assert( isImplicitlyConvertible!(AliasThis3, AliasThis2)); + static assert( isImplicitlyConvertible!(AliasThis3, AliasThis)); + static assert( isImplicitlyConvertible!(AliasThis3, int)); + static assert( isImplicitlyConvertible!(AliasThis3, long)); + static assert(!isImplicitlyConvertible!(AliasThis3, string)); + + // D does not support implicit conversions via construction. + static struct Cons + { + this(int i) + { + this.i = i; + } + + int i; + } + static assert(!isImplicitlyConvertible!(int, Cons)); + + // Classes support implicit conversion based on their class and + // interface hierarchies. + static interface I1 {} + static class Base : I1 {} + + static interface I2 {} + static class Foo : Base, I2 {} + + static class Bar : Base {} + + static assert( isImplicitlyConvertible!(Base, Base)); + static assert(!isImplicitlyConvertible!(Base, Foo)); + static assert(!isImplicitlyConvertible!(Base, Bar)); + static assert( isImplicitlyConvertible!(Base, I1)); + static assert(!isImplicitlyConvertible!(Base, I2)); + + static assert( isImplicitlyConvertible!(Foo, Base)); + static assert( isImplicitlyConvertible!(Foo, Foo)); + static assert(!isImplicitlyConvertible!(Foo, Bar)); + static assert( isImplicitlyConvertible!(Foo, I1)); + static assert( isImplicitlyConvertible!(Foo, I2)); + + static assert( isImplicitlyConvertible!(Bar, Base)); + static assert(!isImplicitlyConvertible!(Bar, Foo)); + static assert( isImplicitlyConvertible!(Bar, Bar)); + static assert( isImplicitlyConvertible!(Bar, I1)); + static assert(!isImplicitlyConvertible!(Bar, I2)); + + static assert(!isImplicitlyConvertible!(I1, Base)); + static assert(!isImplicitlyConvertible!(I1, Foo)); + static assert(!isImplicitlyConvertible!(I1, Bar)); + static assert( isImplicitlyConvertible!(I1, I1)); + static assert(!isImplicitlyConvertible!(I1, I2)); + + static assert(!isImplicitlyConvertible!(I2, Base)); + static assert(!isImplicitlyConvertible!(I2, Foo)); + static assert(!isImplicitlyConvertible!(I2, Bar)); + static assert(!isImplicitlyConvertible!(I2, I1)); + static assert( isImplicitlyConvertible!(I2, I2)); + + // Note that arrays are not implicitly convertible even when their elements + // are implicitly convertible. + static assert(!isImplicitlyConvertible!(ubyte[], uint[])); + static assert(!isImplicitlyConvertible!(Foo[], Base[])); + static assert(!isImplicitlyConvertible!(Bar[], Base[])); + + // However, like with pointers, dynamic arrays are convertible based on + // constness. + static assert( isImplicitlyConvertible!(Base[], const Base[])); + static assert( isImplicitlyConvertible!(Base[], const(Base)[])); + static assert(!isImplicitlyConvertible!(Base[], immutable(Base)[])); + static assert(!isImplicitlyConvertible!(const Base[], immutable Base[])); + static assert( isImplicitlyConvertible!(const Base[], const Base[])); + static assert(!isImplicitlyConvertible!(const Base[], immutable Base[])); +} + +/++ + Whether $(D From) is + $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) + to $(D To). + + This is testing whether $(D From) and $(D To) are the same type - minus the + qualifiers - and whether the qualifiers on $(D From) can be implicitly + converted to the qualifiers on $(D To). No other implicit conversions are + taken into account. + + For instance, $(D const int*) is not implicitly convertible to $(D int*), + because that would violate $(D const). That means that $(D const) is not + qualifier convertible to mutable. And as such, $(I any) $(D const) type + is not qualifier convertible to a mutable type even if it's implicitly + convertible. E.G. $(D const int) is implicitly convertible to $(D int), + because it can be copied to avoid violating $(D const), but it's still not + qualifier convertible, because $(D const) types in general cannot be + implicitly converted to mutable. + + The exact types being tested matter, because they need to be the same + (minus the qualifiers) in order to be considered convertible, but beyond + that, all that matters for the conversion is whether those qualifers would + be convertible regardless of which types they were on. So, if you're having + trouble picturing whether $(D From) would be qualifier convertible to + $(D To), then consider which conversions would be allowed from $(D From[]) + to $(D To[]) (and remember that dynamic arrays are only implicitly + convertible based on their qualifers). + + The $(DDSUBLINK spec/const3, implicit_qualifier_conversions, spec) provides + a table of which qualifiers can be implcitly converted to which other + qualifers (and of course, there a bunch of examples below). + + So, isQualifierConvertible can be used in a case like + $(D isQualifierConvertible!(ReturnType!(typeof(foo(bar))), const char), + which would be testing that the return type of $(D foo(bar)) was $(D char), + $(D const char), or $(D immutable char) (since those are the only types + which are qualifier convertible to $(D const char)). + + This is in contrast to + $(D isImplicitlyConvertible!(ReturnType!(typeof(foo(bar))), const char), + which would be $(D true) for $(I any) type which was implicitly convertible + to $(D const char) rather than just $(D char), $(D const char), and + $(D immutable char). + + See_Also: + $(DDSUBLINK spec/const3, implicit_qualifier_conversions, Spec for implicit qualifier conversions) + $(LREF isImplicitlyConvertible) + +/ +enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is(From* : To*); + +/// +@safe unittest +{ + // i.e. char* -> const char* + static assert( isQualifierConvertible!(char, const char)); + + // i.e. const char* -> char* + static assert(!isQualifierConvertible!(const char, char)); + + static assert( isQualifierConvertible!(int, int)); + static assert( isQualifierConvertible!(int, const int)); + static assert(!isQualifierConvertible!(int, immutable int)); + + static assert(!isQualifierConvertible!(const int, int)); + static assert( isQualifierConvertible!(const int, const int)); + static assert(!isQualifierConvertible!(const int, immutable int)); + + static assert(!isQualifierConvertible!(immutable int, int)); + static assert( isQualifierConvertible!(immutable int, const int)); + static assert( isQualifierConvertible!(immutable int, immutable int)); + + // Note that inout gets a bit weird, since it's only used with function + // parameters, and it's a stand-in for whatever mutability qualifiers the + // type actually has. So, a function parameter that's inout accepts any + // mutability, but you can't actually implicitly convert to inout, because + // it's unknown within the function what the actual mutability of the type + // is. It will differ depending on the function arguments of a specific + // call to that function, so the same code has to work with all combinations + // of mutability qualifiers. + static assert(!isQualifierConvertible!(int, inout int)); + static assert(!isQualifierConvertible!(const int, inout int)); + static assert(!isQualifierConvertible!(immutable int, inout int)); + static assert( isQualifierConvertible!(inout int, inout int)); + + static assert(!isQualifierConvertible!(inout int, int)); + static assert( isQualifierConvertible!(inout int, const int)); + static assert(!isQualifierConvertible!(inout int, immutable int)); + + // shared is of course also a qualifer. + static assert(!isQualifierConvertible!(int, shared int)); + static assert(!isQualifierConvertible!(int, const shared int)); + static assert(!isQualifierConvertible!(const int, shared int)); + static assert(!isQualifierConvertible!(const int, const shared int)); + static assert(!isQualifierConvertible!(immutable int, shared int)); + static assert( isQualifierConvertible!(immutable int, const shared int)); + + static assert(!isQualifierConvertible!(shared int, int)); + static assert(!isQualifierConvertible!(shared int, const int)); + static assert(!isQualifierConvertible!(shared int, immutable int)); + static assert( isQualifierConvertible!(shared int, shared int)); + static assert( isQualifierConvertible!(shared int, const shared int)); + + static assert(!isQualifierConvertible!(const shared int, int)); + static assert(!isQualifierConvertible!(const shared int, const int)); + static assert(!isQualifierConvertible!(const shared int, immutable int)); + static assert(!isQualifierConvertible!(const shared int, shared int)); + static assert( isQualifierConvertible!(const shared int, const shared int)); + + // Implicit conversions don't count unless they're based purely on + // qualifiers. + enum E : int + { + a = 1 + } + + static assert(!isQualifierConvertible!(E, int)); + static assert(!isQualifierConvertible!(E, const int)); + static assert( isQualifierConvertible!(E, E)); + static assert( isQualifierConvertible!(E, const E)); + static assert(!isQualifierConvertible!(E, immutable E)); + + static struct AliasThis + { + int i; + alias this = i; + } + + static assert(!isQualifierConvertible!(AliasThis, int)); + static assert(!isQualifierConvertible!(AliasThis, const int)); + static assert( isQualifierConvertible!(AliasThis, AliasThis)); + static assert( isQualifierConvertible!(AliasThis, const AliasThis)); + static assert(!isQualifierConvertible!(AliasThis, immutable AliasThis)); + + // The qualifiers are irrelevant if the types aren't the same when + // stripped of all qualifers. + static assert(!isQualifierConvertible!(int, long)); + static assert(!isQualifierConvertible!(int, const long)); + static assert(!isQualifierConvertible!(string, const(ubyte)[])); +} + +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + alias Types = AliasSeq!(int, const int, shared int, inout int, const shared int, + const inout int, inout shared int, const inout shared int, immutable int); + + // https://dlang.org/spec/const3.html#implicit_qualifier_conversions + enum _ = 0; + static immutable bool[Types.length][Types.length] conversions = [ + // m c s i cs ci is cis im + [1, 1, _, _, _, _, _, _, _], // mutable + [_, 1, _, _, _, _, _, _, _], // const + [_, _, 1, _, 1, _, _, _, _], // shared + [_, 1, _, 1, _, 1, _, _, _], // inout + [_, _, _, _, 1, _, _, _, _], // const shared + [_, 1, _, _, _, 1, _, _, _], // const inout + [_, _, _, _, 1, _, 1, 1, _], // inout shared + [_, _, _, _, 1, _, _, 1, _], // const inout shared + [_, 1, _, _, 1, 1, _, 1, 1], // immutable + ]; + + foreach (i, From; Types) + { + foreach (j, To; Types) + { + static assert(isQualifierConvertible!(From, To) == conversions[i][j], + "`isQualifierConvertible!(" ~ From.stringof ~ ", " ~ To.stringof ~ ")`" ~ + " should be `" ~ (conversions[i][j] ? "true`" : "false`")); + } + } +} + /++ Removes the outer layer of $(D const), $(D inout), or $(D immutable) from type $(D T). @@ -1388,7 +1801,7 @@ else T* ptr; } - // The qualifer on the type is removed, but the qualifier on the template + // The qualifier on the type is removed, but the qualifier on the template // argument is not. static assert(is(Unconst!(const(Foo!(const int))) == Foo!(const int))); static assert(is(Unconst!(Foo!(const int)) == Foo!(const int))); @@ -1497,7 +1910,7 @@ template Unshared(T) T* ptr; } - // The qualifer on the type is removed, but the qualifier on the template + // The qualifier on the type is removed, but the qualifier on the template // argument is not. static assert(is(Unshared!(shared(Foo!(shared int))) == Foo!(shared int))); static assert(is(Unshared!(Foo!(shared int)) == Foo!(shared int))); @@ -1552,7 +1965,7 @@ template Unshared(T) really should use $(LREF Unconst) instead. But of course, if a template constraint or $(D static if) really needs to - strip off both the mutability qualifers and $(D shared) for what it's + strip off both the mutability qualifiers and $(D shared) for what it's testing for, then that's what Unqualified is for. It's just that it's best practice to use $(LREF Unconst) when it's not clear that $(D shared) should be removed as well. @@ -1637,7 +2050,7 @@ else T* ptr; } - // The qualifers on the type are removed, but the qualifiers on the + // The qualifiers on the type are removed, but the qualifiers on the // template argument are not. static assert(is(Unqualified!(const(Foo!(const int))) == Foo!(const int))); static assert(is(Unqualified!(Foo!(const int)) == Foo!(const int))); From e10c49f528dec9e79fe6e63fbd81a5f55f4463b9 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 22 Mar 2024 03:06:43 -0600 Subject: [PATCH 28/57] Add Filter and Reverse to phobos.sys.meta. They're the same as their std.meta counterparts, albeit with tweaked documentation and examples. I also fixed the casing on Map's parameters. They're supposed to be types (or a template in the case of the template that it's applying to the other arguments), so they should be PascalCased. --- phobos/sys/meta.d | 71 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/phobos/sys/meta.d b/phobos/sys/meta.d index c57b7714cc3..85988ba5451 100644 --- a/phobos/sys/meta.d +++ b/phobos/sys/meta.d @@ -53,8 +53,12 @@ $(LREF Alias) $(LREF AliasSeq) )) + $(TR $(TD Alias sequence filtering) $(TD + $(LREF Filter) + )) $(TR $(TD Alias sequence transformation) $(TD $(LREF Map) + $(LREF Reverse) )) ) @@ -199,17 +203,49 @@ alias Alias(T) = T; } /++ - Map takes a template predicate and applies it to every element in the given + Filters an $(D AliasSeq) using the given template predicate. + + The result is an $(D AliasSeq) that contains only the elements which satisfy + the predicate. + +/ +template Filter(alias Pred, Args...) +{ + alias Filter = AliasSeq!(); + static foreach (Arg; Args) + { + static if (Pred!Arg) + Filter = AliasSeq!(Filter, Arg); + } +} + +/// +@safe unittest +{ + import phobos.sys.traits : isDynamicArray, isPointer, isUnsignedInteger; + + alias Types = AliasSeq!(string, int, int[], bool[], ulong, double, ubyte); + + static assert(is(Filter!(isDynamicArray, Types) == + AliasSeq!(string, int[], bool[]))); + + static assert(is(Filter!(isUnsignedInteger, Types) == + AliasSeq!(ulong, ubyte))); + + static assert(is(Filter!(isPointer, Types) == AliasSeq!())); +} + +/++ + Map takes a template and applies it to every element in the given $(D AliasSeq), resulting in an $(D AliasSeq) with the transformed elements. So, it's equivalent to - `AliasSeq!(fun!(args[0]), fun!(args[1]), ..., fun!(args[$ - 1]))`. + `AliasSeq!(Fun!(Args[0]), Fun!(Args[1]), ..., Fun!(Args[$ - 1]))`. +/ -template Map(alias fun, args...) +template Map(alias Fun, Args...) { alias Map = AliasSeq!(); - static foreach (arg; args) - Map = AliasSeq!(Map, fun!arg); + static foreach (Arg; Args) + Map = AliasSeq!(Map, Fun!Arg); } /// @@ -258,3 +294,28 @@ template Map(alias fun, args...) assert(A == typeid(int)); } + +/++ + Takes an $(D AliasSeq) and result in an $(D AliasSeq) where the order of + the elements has been reversed. + +/ +template Reverse(Args...) +{ + alias Reverse = AliasSeq!(); + static foreach_reverse (Arg; Args) + Reverse = AliasSeq!(Reverse, Arg); +} + +/// +@safe unittest +{ + static assert(is(Reverse!(int, byte, long, string) == + AliasSeq!(string, long, byte, int))); + + alias Types = AliasSeq!(int, long, long, int, float, + ubyte, short, ushort, uint); + static assert(is(Reverse!Types == AliasSeq!(uint, ushort, short, ubyte, + float, int, long, long, int))); + + static assert(is(Reverse!() == AliasSeq!())); +} From 676710c7b83472d54c2339cfa89f182fb0cfb71f Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 23 Mar 2024 01:46:09 -0600 Subject: [PATCH 29/57] std/math/hardware.d: use an alternative register naming ... ... for LoongArch64 so that LDC/LLVM can recognize certain fp instructions --- std/math/hardware.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/math/hardware.d b/std/math/hardware.d index f474ea90373..4bfe792428c 100644 --- a/std/math/hardware.d +++ b/std/math/hardware.d @@ -203,7 +203,7 @@ private: { asm nothrow @nogc { - "movgr2fcsr $r2,$r0"; + "movgr2fcsr $fcsr2,$r0"; } } else @@ -933,7 +933,7 @@ private: { asm nothrow @nogc { - "movgr2fcsr $r0,%0" : + "movgr2fcsr $fcsr0,%0" : : "r" (newState & (roundingMask | allExceptions)); } } From a613e8f7147ecbffffb5bf32b5c52e4591cf5fd8 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Tue, 26 Mar 2024 19:11:20 -0600 Subject: [PATCH 30/57] Add all and any to phobos.sys.meta. (#8959) all is the same as std.meta's allSatisfy, and any is the same as std.meta's anySatisfy. However, their documentation and examples have been tweaked. --- phobos/sys/meta.d | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/phobos/sys/meta.d b/phobos/sys/meta.d index 85988ba5451..2fc2483de72 100644 --- a/phobos/sys/meta.d +++ b/phobos/sys/meta.d @@ -60,6 +60,10 @@ $(LREF Map) $(LREF Reverse) )) + $(TR $(TD Alias sequence searching) $(TD + $(LREF all) + $(LREF any) + )) ) References: @@ -319,3 +323,73 @@ template Reverse(Args...) static assert(is(Reverse!() == AliasSeq!())); } + +/++ + Whether the given template predicate is $(D true) for all of the elements in + the given $(D AliasSeq). + + Evaluation is $(I not) short-circuited if a $(D false) result is + encountered; the template predicate must be instantiable with all the + elements. + +/ +version (StdDdoc) template all(alias Pred, Args...) +{ + import core.internal.traits : allSatisfy; + alias all = allSatisfy!(Pred, Args); +} +else +{ + import core.internal.traits : allSatisfy; + alias all = allSatisfy; +} + +/// +@safe unittest +{ + import phobos.sys.traits : isDynamicArray, isInteger; + + static assert(!all!(isInteger, int, double)); + static assert( all!(isInteger, int, long)); + + alias Types = AliasSeq!(string, int[], bool[]); + + static assert( all!(isDynamicArray, Types)); + static assert(!all!(isInteger, Types)); + + static assert( all!isInteger); +} + +/++ + Whether the given template predicate is $(D true) for any of the elements in + the given $(D AliasSeq). + + Evaluation is $(I not) short-circuited if a $(D true) result is + encountered; the template predicate must be instantiable with all the + elements. + +/ +version (StdDdoc) template any(alias Pred, Args...) +{ + import core.internal.traits : anySatisfy; + alias any = anySatisfy!(Pred, Args); +} +else +{ + import core.internal.traits : anySatisfy; + alias any = anySatisfy; +} + +/// +@safe unittest +{ + import phobos.sys.traits : isDynamicArray, isInteger; + + static assert(!any!(isInteger, string, double)); + static assert( any!(isInteger, int, double)); + + alias Types = AliasSeq!(string, int[], bool[], real, bool); + + static assert( any!(isDynamicArray, Types)); + static assert(!any!(isInteger, Types)); + + static assert(!any!isInteger); +} From ec0857574c40839fc152a24ccb228565c1330d0f Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 27 Mar 2024 03:33:54 -0600 Subject: [PATCH 31/57] Add isSameSymbol and isSameType to v3 traits and indexOf to v3 meta. (#8960) indexOf is phobos.sys.meta's version of std.meta's staticIndexOf. The name was changed to better match our naming scheme. Because the result of the template is a value and not a type or AliasSeq, it is correct for it to be camelCased, but we don't normally prepend templates with static, making it inconsistent to do so in the case of indexOf. This might result in some symbol conflicts with indexOf from the algorithm modules (assuming that we retain that function and its name for Phobos v3), but a quick test showed that importing both phobos.sys.meta and std.algorithm didn't result in a symbol conflict when using the one from phobos.sys.meta. And even if we do get symbol conflicts in some situations, the module system is designed to allow us to deal with that. As for the implementation, I've both made indexOf more flexible and more straightforward. staticIndexOf looks for an element in the AliasSeq which is "the same" as the first argument, and that results in a big mess, since what "the same" is varies considerably depending on what the elements are, and staticIndexOf makes it even more complicated by evaluating some symbols at CTFE if it can (e.g. evaluating a no-arg function that returns an int to try to compare it to an integer literal) while not evaluating them in other cases. Not only did trying to document the current behavior make it clear that it's just way too confusing, but even trying to come up with a sane simplification of how the comparison works was just too messy, because it's trying to be able to compare just about anything which you can stick in an AliasSeq. So, I punted on it by taking the comparison out of the equation entirely. indexOf now takes a template predicate. So, rather than looking for an element which is the same, it looks for an element which matches the predicate. This allows indexOf to be used in more cases than staticIndexOf can be, _and_ it allows the programmer using it to decide how the comparison works by choosing a predicate that matches what they want. So, in conjuction with that I added isSameSymbol and isSameType to phobos.sys.traits, since those should correspond to the most common searches that someone would be trying to do with staticIndexOf, but since those traits are very specific in what they search for rather than searching for an element which is "the same" in some nebulous sense, the code should end up being much clearer and cleaner. And if someone wants to do something completely different like indexOf!(isInteger, Types), then they can, unlike with staticIndexOf. --- phobos/sys/meta.d | 79 ++++++++++++++++++++++++++ phobos/sys/traits.d | 131 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/phobos/sys/meta.d b/phobos/sys/meta.d index 2fc2483de72..2236dc89c7f 100644 --- a/phobos/sys/meta.d +++ b/phobos/sys/meta.d @@ -63,6 +63,7 @@ $(TR $(TD Alias sequence searching) $(TD $(LREF all) $(LREF any) + $(LREF indexOf) )) ) @@ -393,3 +394,81 @@ else static assert(!any!isInteger); } + +/++ + Returns the index of the first element where $(D Pred!(Args[i])) is + $(D true). + + If $(D Pred!(Args[i])) is not $(D true) for any elements, then the result + is $(D -1). + + Evaluation is $(I not) short-circuited if a $(D true) result is + encountered; the template predicate must be instantiable with all the + elements. + +/ +template indexOf(alias Pred, Args...) +{ + enum ptrdiff_t indexOf = + { + static foreach (i; 0 .. Args.length) + { + static if (Pred!(Args[i])) + return i; + } + return -1; + }(); +} + +/// +@safe unittest +{ + import phobos.sys.traits : isInteger, isSameSymbol, isSameType; + + alias Types1 = AliasSeq!(string, int, long, char[], ubyte, int); + alias Types2 = AliasSeq!(float, double, int[], char[], void); + + static assert(indexOf!(isInteger, Types1) == 1); + static assert(indexOf!(isInteger, Types2) == -1); + + static assert(indexOf!(isSameType!ubyte, Types1) == 4); + static assert(indexOf!(isSameType!ubyte, Types2) == -1); + + int i; + int j; + string s; + int foo() { return 0; } + alias Symbols = AliasSeq!(i, j, foo); + static assert(indexOf!(isSameSymbol!j, Symbols) == 1); + static assert(indexOf!(isSameSymbol!s, Symbols) == -1); + + // Empty AliasSeq. + static assert(indexOf!isInteger == -1); + + // The predicate does not compile with all of the arguments, + // so indexOf does not compile. + static assert(!__traits(compiles, indexOf!(isSameType!int, long, int, 42))); +} + +unittest +{ + import phobos.sys.traits : isSameType; + + static assert(indexOf!(isSameType!int, short, int, long) >= 0); + static assert(indexOf!(isSameType!string, short, int, long) < 0); + + // This is to verify that we don't accidentally end up with the type of + // the result differing based on whether it's -1 or not. Not specifying the + // type at all in indexOf results in -1 being int on all systems and the + // other results being whatever size_t is (ulong on most systems at this + // point), which does generally work, but being explicit with the type + // avoids any subtle issues that might come from the type of the result + // varying based on whether the item is found or not. + static assert(is(typeof(indexOf!(isSameType!int, short, int, long)) == + typeof(indexOf!(isSameType!string, short, int, long)))); + + static assert(indexOf!(isSameType!string, string, string, string, string) == 0); + static assert(indexOf!(isSameType!string, int, string, string, string) == 1); + static assert(indexOf!(isSameType!string, int, int, string, string) == 2); + static assert(indexOf!(isSameType!string, int, int, int, string) == 3); + static assert(indexOf!(isSameType!string, int, int, int, int) == -1); +} diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 24788bb5ff9..324b63bb23f 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -51,7 +51,6 @@ $(BOOKTABLE , $(TR $(TH Category) $(TH Templates)) $(TR $(TD Categories of types) $(TD - $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF isDynamicArray) $(LREF isFloatingPoint) $(LREF isInteger) @@ -65,6 +64,10 @@ $(LREF isImplicitlyConvertible) $(LREF isQualifierConvertible) )) + $(TR $(TD Traits for comparisons) $(TD + $(LREF isSameSymbol) + $(LREF isSameType) + )) $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) $(LREF Unshared) @@ -1702,6 +1705,132 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is } } +/++ + Whether the given symbols are the same symbol. + + All this does is $(D __traits(isSame, lhs, rhs)), so most code shouldn't + use it. It's intended to be used in conjunction with templates that take a + template predicate - such as those in phobos.sys.meta. + + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + + See_Also: + $(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, lhs, rhs))) + $(LREF isSameType) + +/ +enum isSameSymbol(alias lhs, alias rhs) = __traits(isSame, lhs, rhs); + +/++ Ditto +/ +template isSameSymbol(alias lhs) +{ + enum isSameSymbol(alias rhs) = __traits(isSame, lhs, rhs); +} + +/// +@safe unittest +{ + int i; + int j; + real r; + + static assert( isSameSymbol!(i, i)); + static assert(!isSameSymbol!(i, j)); + static assert(!isSameSymbol!(i, r)); + + static assert(!isSameSymbol!(j, i)); + static assert( isSameSymbol!(j, j)); + static assert(!isSameSymbol!(j, r)); + + static assert(!isSameSymbol!(r, i)); + static assert(!isSameSymbol!(r, j)); + static assert( isSameSymbol!(r, r)); + + auto foo() { return 0; } + auto bar() { return 0; } + + static assert( isSameSymbol!(foo, foo)); + static assert(!isSameSymbol!(foo, bar)); + static assert(!isSameSymbol!(foo, i)); + + static assert(!isSameSymbol!(bar, foo)); + static assert( isSameSymbol!(bar, bar)); + static assert(!isSameSymbol!(bar, i)); + + // Types are symbols too. However, in most cases, they should be compared + // as types, not symbols (be it with is expressions or with isSameType), + // because the results aren't consistent between scalar types and + // user-defined types with regards to type qualifiers when they're compared + // as symbols. + static assert( isSameSymbol!(double, double)); + static assert(!isSameSymbol!(double, const double)); + static assert(!isSameSymbol!(double, int)); + static assert( isSameSymbol!(Object, Object)); + static assert( isSameSymbol!(Object, const Object)); + + static assert(!isSameSymbol!(i, int)); + static assert( isSameSymbol!(typeof(i), int)); + + // Lambdas can be compared with __traits(isSame, ...), + // so they can be compared with isSameSymbol. + static assert( isSameSymbol!(a => a + 42, a => a + 42)); + static assert(!isSameSymbol!(a => a + 42, a => a + 99)); + + // Partial instantiation allows it to be used with templates that expect + // a predicate that takes only a single argument. + import phobos.sys.meta : AliasSeq, indexOf; + alias Types = AliasSeq!(i, j, r, int, long, foo); + static assert(indexOf!(isSameSymbol!j, Types) == 1); + static assert(indexOf!(isSameSymbol!int, Types) == 3); + static assert(indexOf!(isSameSymbol!bar, Types) == -1); +} + +/++ + Whether the given types are the same type. + + All this does is $(D is(T == U)), so most code shouldn't use it. It's + intended to be used in conjunction with templates that take a template + predicate - such as those in phobos.sys.meta. + + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + + See_Also: + $(LREF isSameSymbol) + +/ +enum isSameType(T, U) = is(T == U); + +/++ Ditto +/ +template isSameType(T) +{ + enum isSameType(U) = is(T == U); +} + +/// +@safe unittest +{ + static assert( isSameType!(long, long)); + static assert(!isSameType!(long, const long)); + static assert(!isSameType!(long, string)); + static assert( isSameType!(string, string)); + + int i; + real r; + static assert( isSameType!(int, typeof(i))); + static assert(!isSameType!(int, typeof(r))); + + static assert(!isSameType!(real, typeof(i))); + static assert( isSameType!(real, typeof(r))); + + // Partial instantiation allows it to be used with templates that expect + // a predicate that takes only a single argument. + import phobos.sys.meta : AliasSeq, indexOf; + alias Types = AliasSeq!(float, string, int, double); + static assert(indexOf!(isSameType!int, Types) == 2); +} + /++ Removes the outer layer of $(D const), $(D inout), or $(D immutable) from type $(D T). From eeffdfe525ba41a0d367802d27af0eb058ccf262 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 28 Mar 2024 00:05:00 -0600 Subject: [PATCH 32/57] Add Stride and Unique to phobos.sys.meta. Stride is basically the same as std.meta's Stride aside from some improvements to the tests and documentation. Unique is the equivalent of std.meta's NoDuplicates except that it's been updated in the same fashion as indexOf. So, instead of checking for whether the elements are "the same" based on some complicated rules, Unique takes a predicate which it uses to compare the elements for equality (essentially making it the AliasSeq equivalent of std.algorithm's uniq, though uniq requires that the range be sorted, and Unique has no such requirement). So, the programmer can decide what kind of comparison is used. --- phobos/sys/meta.d | 202 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 1 deletion(-) diff --git a/phobos/sys/meta.d b/phobos/sys/meta.d index 2236dc89c7f..e28a4d1c1ce 100644 --- a/phobos/sys/meta.d +++ b/phobos/sys/meta.d @@ -55,6 +55,8 @@ )) $(TR $(TD Alias sequence filtering) $(TD $(LREF Filter) + $(LREF Stride) + $(LREF Unique) )) $(TR $(TD Alias sequence transformation) $(TD $(LREF Map) @@ -239,6 +241,204 @@ template Filter(alias Pred, Args...) static assert(is(Filter!(isPointer, Types) == AliasSeq!())); } +/++ + Evaluates to an $(LREF AliasSeq) which only contains every nth element from + the $(LREF AliasSeq) that was passed in, where $(D n) is stepSize. + + So, if stepSize is $(D 2), then the result contains every other element from + the original. If stepSize is $(D 3), then the result contains every third + element from the original. Etc. + + If stepSize is negative, then the result is equivalent to using + $(LREF Reverse) on the given $(LREF AliasSeq) and then using Stride on it + with the absolute value of that stepSize. + + If stepSize is positive, then the first element in the original + $(LREF AliasSeq) is the first element in the result, whereas if stepSize is + negative, then the last element in the original is the first element in the + result. Each subsequent element is then the element at the index of the + previous element plus stepSize. + +/ +template Stride(int stepSize, Args...) +if (stepSize != 0) +{ + alias Stride = AliasSeq!(); + static if (stepSize > 0) + { + static foreach (i; 0 .. (Args.length + stepSize - 1) / stepSize) + Stride = AliasSeq!(Stride, Args[i * stepSize]); + } + else + { + static foreach (i; 0 .. (Args.length - stepSize - 1) / -stepSize) + Stride = AliasSeq!(Stride, Args[$ - 1 + i * stepSize]); + } +} + +/// +@safe unittest +{ + static assert(is(Stride!(1, short, int, long) == AliasSeq!(short, int, long))); + static assert(is(Stride!(2, short, int, long) == AliasSeq!(short, long))); + static assert(is(Stride!(3, short, int, long) == AliasSeq!short)); + static assert(is(Stride!(100, short, int, long) == AliasSeq!short)); + + static assert(is(Stride!(-1, short, int, long) == AliasSeq!(long, int, short))); + static assert(is(Stride!(-2, short, int, long) == AliasSeq!(long, short))); + static assert(is(Stride!(-3, short, int, long) == AliasSeq!long)); + static assert(is(Stride!(-100, short, int, long) == AliasSeq!long)); + + alias Types = AliasSeq!(short, int, long, ushort, uint, ulong); + static assert(is(Stride!(3, Types) == AliasSeq!(short, ushort))); + static assert(is(Stride!(3, Types[1 .. $]) == AliasSeq!(int, uint))); + static assert(is(Stride!(-3, Types) == AliasSeq!(ulong, long))); + + static assert(is(Stride!(-2, Types) == Stride!(2, Reverse!Types))); + + static assert(is(Stride!1 == AliasSeq!())); + static assert(is(Stride!100 == AliasSeq!())); +} + +@safe unittest +{ + static assert(!__traits(compiles, Stride!(0, int))); + + alias Types = AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, + char, wchar, dchar, float, double, real, Object); + alias Types2 = AliasSeq!(bool, ubyte, ushort, uint, ulong, wchar, float, real); + alias Types3 = AliasSeq!(bool, short, uint, char, float, Object); + alias Types4 = AliasSeq!(bool, ushort, ulong, float); + alias Types5 = AliasSeq!(bool, int, wchar, Object); + alias Types6 = AliasSeq!(bool, uint, float); + alias Types7 = AliasSeq!(bool, long, real); + alias Types8 = AliasSeq!(bool, ulong); + alias Types9 = AliasSeq!(bool, char); + alias Types10 = AliasSeq!(bool, wchar); + + static assert(is(Stride!(1, Types) == Types)); + static assert(is(Stride!(2, Types) == Types2)); + static assert(is(Stride!(3, Types) == Types3)); + static assert(is(Stride!(4, Types) == Types4)); + static assert(is(Stride!(5, Types) == Types5)); + static assert(is(Stride!(6, Types) == Types6)); + static assert(is(Stride!(7, Types) == Types7)); + static assert(is(Stride!(8, Types) == Types8)); + static assert(is(Stride!(9, Types) == Types9)); + static assert(is(Stride!(10, Types) == Types10)); + + static assert(is(Stride!(-1, Types) == Reverse!Types)); + static assert(is(Stride!(-2, Types) == Stride!(2, Reverse!Types))); + static assert(is(Stride!(-3, Types) == Stride!(3, Reverse!Types))); + static assert(is(Stride!(-4, Types) == Stride!(4, Reverse!Types))); + static assert(is(Stride!(-5, Types) == Stride!(5, Reverse!Types))); + static assert(is(Stride!(-6, Types) == Stride!(6, Reverse!Types))); + static assert(is(Stride!(-7, Types) == Stride!(7, Reverse!Types))); + static assert(is(Stride!(-8, Types) == Stride!(8, Reverse!Types))); + static assert(is(Stride!(-9, Types) == Stride!(9, Reverse!Types))); + static assert(is(Stride!(-10, Types) == Stride!(10, Reverse!Types))); +} + +/++ + Evaluates to an $(LREF AliasSeq) which contains no duplicate elements. + + Unique takes a binary template predicate that it uses to compare elements + for equality. If the predicate is $(D true) when an element in the given + $(LREF AliasSeq) is compared with an element with a lower index, then that + element is not included in the result (so if any elements in the + $(LREF AliasSeq) are considered equal per the predicate, then only the + first one is included in the result). + + Note that the binary predicate must be partially instantiable, e.g. + --- + alias PartialCmp = Cmp!(Args[0]); + enum same = PartialCmp!(Args[1]); + --- + Otherwise, it won't work. + + See_Also: + $(REF isSameSymbol, phobos, sys, traits) + $(REF isSameType, phobos, sys, traits) + +/ +template Unique(alias Cmp, Args...) +{ + alias Unique = AliasSeq!(); + static foreach (i, Arg; Args) + { + static if (i == 0) + Unique = AliasSeq!Arg; + else + Unique = AppendIfUnique!(Cmp, Unique, Arg); + } +} + +// Unfortunately, this can't be done in-place in Unique, because then we get +// errors about reassigning Unique after reading it. +private template AppendIfUnique(alias Cmp, Args...) +{ + static if (indexOf!(Cmp!(Args[$ - 1]), Args[0 .. $ - 1]) == -1) + alias AppendIfUnique = Args; + else + alias AppendIfUnique = Args[0 .. $ - 1]; +} + +/// +@safe unittest +{ + import phobos.sys.traits : isSameType; + + alias Types1 = AliasSeq!(int, long, long, int, int, float, int); + + static assert(is(Unique!(isSameType, Types1) == + AliasSeq!(int, long, float))); + + alias Types2 = AliasSeq!(byte, ubyte, short, ushort, int, uint); + static assert(is(Unique!(isSameType, Types2) == Types2)); + + // Empty AliasSeq. + static assert(Unique!isSameType.length == 0); + + // An AliasSeq with a single element works as well. + static assert(Unique!(isSameType, int).length == 1); +} + +/// +@safe unittest +{ + import phobos.sys.traits : isSameSymbol; + + int i; + string s; + real r; + alias Symbols = AliasSeq!(i, s, i, i, s, r, r, i); + + alias Result = Unique!(isSameSymbol, Symbols); + static assert(Result.length == 3); + static assert(__traits(isSame, Result[0], i)); + static assert(__traits(isSame, Result[1], s)); + static assert(__traits(isSame, Result[2], r)); + + // Comparing AliasSeqs for equality with is expressions only works + // if they only contain types. + static assert(!is(Symbols == Result)); +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, uint, long, string, short, int*, ushort); + + template sameSize(T) + { + enum sameSize(U) = T.sizeof == U.sizeof; + } + static assert(is(Unique!(sameSize, Types) == + AliasSeq!(int, long, string, short))); + + // The predicate must be partially instantiable. + enum sameSize_fails(T, U) = T.sizeof == U.sizeof; + static assert(!__traits(compiles, Unique!(sameSize_fails, Types))); +} + /++ Map takes a template and applies it to every element in the given $(D AliasSeq), resulting in an $(D AliasSeq) with the transformed elements. @@ -396,7 +596,7 @@ else } /++ - Returns the index of the first element where $(D Pred!(Args[i])) is + Evaluates to the index of the first element where $(D Pred!(Args[i])) is $(D true). If $(D Pred!(Args[i])) is not $(D true) for any elements, then the result From 14b23633b762cfd7b03614dca4c6b0cafa1016e5 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sun, 31 Mar 2024 20:25:24 +0100 Subject: [PATCH 33/57] [std.traits] Improve FunctionTypeOf docs --- std/traits.d | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/std/traits.d b/std/traits.d index 2e7a4f6a30b..5ed37a1f418 100644 --- a/std/traits.d +++ b/std/traits.d @@ -2181,7 +2181,7 @@ if (isCallable!func) /** -Get the function type from a callable object `func`. +Get the function type from a callable object `func`, or from a function pointer/delegate type. Using builtin `typeof` on a property function yields the types of the property value, not of the property function itself. Still, @@ -2229,10 +2229,17 @@ if (isCallable!func) { class C { - int value() @property { return 0; } + int value() @property => 0; + static string opCall() => "hi"; } static assert(is( typeof(C.value) == int )); static assert(is( FunctionTypeOf!(C.value) == function )); + static assert(is( FunctionTypeOf!C == typeof(C.opCall) )); + + int function() fp; + alias IntFn = int(); + static assert(is( typeof(fp) == IntFn* )); + static assert(is( FunctionTypeOf!fp == IntFn )); } @system unittest From 8729740e3221cd6dcccdbbbb12b452d0ee9c1ee1 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Mon, 1 Apr 2024 23:52:32 +0200 Subject: [PATCH 34/57] Mark `endianToNativeImpl` `@trusted` --- std/bitmanip.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/bitmanip.d b/std/bitmanip.d index 3006d6337ad..717fa2dbb7b 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -3274,7 +3274,7 @@ if (canSwapEndianness!T && n == T.sizeof) assert(c == littleEndianToNative!dchar(swappedC)); } -private T endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @nogc nothrow pure @safe +private T endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @nogc nothrow pure @trusted if (__traits(isIntegral, T) && n == T.sizeof) { if (!__ctfe) From 08b6dd3f7a24ac94ffd5ae2541bccacb3048f757 Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Tue, 2 Apr 2024 15:46:44 +0200 Subject: [PATCH 35/57] Fix bugzilla 24478 - std.csv array out of bounds when row size exceeds header --- std/csv.d | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/std/csv.d b/std/csv.d index 7f5c2b24c01..9ee9b5d5baf 100644 --- a/std/csv.d +++ b/std/csv.d @@ -175,6 +175,16 @@ class CSVException : Exception assert(ex.toString == "(Row: 1, Col: 2) Unexpected 'b' when converting from type string to type int"); } +// https://issues.dlang.org/show_bug.cgi?id=24478 +@safe unittest +{ + import std.exception : collectException; + import std.algorithm.searching : count; + string text = "A, B\n1, 2, 3"; + auto ex = collectException!CSVException(csvReader!(string[string])(text, null).count); + assert(ex.toString == "(Row: 1, Col: 3) row contains more values than header"); +} + @safe pure unittest { import std.string; @@ -1179,7 +1189,10 @@ public: { for (; !recordRange.empty; recordRange.popFront()) { - aa[header[_input.col-1]] = recordRange.front; + const i = _input.col - 1; + if (i >= header.length) + throw new CSVException("row contains more values than header", _input.row, _input.col); + aa[header[i]] = recordRange.front; } } catch (ConvException e) From 23e5d21d38462c9dd85a8007394b04e5ea9ce29c Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 5 Apr 2024 14:28:03 -0600 Subject: [PATCH 36/57] Add isEqual and EnumMembers to phobos.sys.traits. (#8970) EnumMembers is obviously needed and does the same thing as its std.traits counterpart. isEqual is new, and it really shouldn't be used in many circumstances, but it's needed in conjunction with Unique to be able to do what NoDuplicates from std.meta does when given an AliasSeq of enum members. So, if you need the list of enum members to have no duplicate values (e.g. when creating a final switch), then you would now do Unique!(isEqual, EnumMembers!E) instead of NoDuplicates!(EnumMembers!E). As part of isEqual's documentation, I added a list of examples which highlight the difference between operating on the list of enum members as an AliasSeq and operating on them as a dynamic array, since that's not something that's at all obvious - and it shows why you might need to use isEqual with Unique to weed out duplicate values instead of doing something like [EnumMembers!E].sort().unique() to weed them out. For documentation purposes, I just assumed that uniq would be renamed to unique, but the documentation can be fixed later if need be once we have the actual functions in Phobos v3. --- phobos/sys/traits.d | 312 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 324b63bb23f..cfc2ac27425 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -60,11 +60,15 @@ $(LREF isStaticArray) $(LREF isUnsignedInteger) )) + $(TR $(TD Aggregate Type traits) $(TD + $(LREF EnumMembers) + )) $(TR $(TD Traits testing for type conversions) $(TD $(LREF isImplicitlyConvertible) $(LREF isQualifierConvertible) )) $(TR $(TD Traits for comparisons) $(TD + $(LREF isEqual) $(LREF isSameSymbol) $(LREF isSameType) )) @@ -1296,6 +1300,172 @@ enum isPointer(T) = is(T == U*, U); } } +/++ + Evaluates to an $(D AliasSeq) containing the members of an enum type. + + The elements of the $(D AliasSeq) are in the same order as they are in the + enum declaration. + + An enum can have multiple members with the same value, so if code needs the + enum values to be unique (e.g. if it's generating a switch statement from + them), then $(REF Unique, phobos, sys, meta) can be used to filter out the + duplicate values - e.g. $(D Unique!(isEqual, EnumMembers!E)). + +/ +template EnumMembers(E) +if (is(E == enum)) +{ + import phobos.sys.meta : AliasSeq; + + alias EnumMembers = AliasSeq!(); + static foreach (member; __traits(allMembers, E)) + EnumMembers = AliasSeq!(EnumMembers, __traits(getMember, E, member)); +} + +/// Create an array of enum values. +@safe unittest +{ + enum Sqrts : real + { + one = 1, + two = 1.41421, + three = 1.73205 + } + auto sqrts = [EnumMembers!Sqrts]; + assert(sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]); +} + +/++ + A generic function $(D rank(v)) in the following example uses this template + for finding a member $(D e) in an enum type $(D E). + +/ +@safe unittest +{ + // Returns i if e is the i-th member of E. + static size_t rank(E)(E e) + if (is(E == enum)) + { + static foreach (i, member; EnumMembers!E) + { + if (e == member) + return i; + } + assert(0, "Not an enum member"); + } + + enum Mode + { + read = 1, + write = 2, + map = 4 + } + assert(rank(Mode.read) == 0); + assert(rank(Mode.write) == 1); + assert(rank(Mode.map) == 2); +} + +/// Use EnumMembers to generate a switch statement using static foreach. +@safe unittest +{ + static class Foo + { + string calledMethod; + void foo() @safe { calledMethod = "foo"; } + void bar() @safe { calledMethod = "bar"; } + void baz() @safe { calledMethod = "baz"; } + } + + enum FuncName : string { foo = "foo", bar = "bar", baz = "baz" } + + auto foo = new Foo; + + s: final switch (FuncName.bar) + { + static foreach (member; EnumMembers!FuncName) + { + // Generate a case for each enum value. + case member: + { + // Call foo.{enum value}(). + __traits(getMember, foo, member)(); + break s; + } + } + } + + // Since we passed FuncName.bar to the switch statement, the bar member + // function was called. + assert(foo.calledMethod == "bar"); +} + +@safe unittest +{ + { + enum A { a } + static assert([EnumMembers!A] == [A.a]); + enum B { a, b, c, d, e } + static assert([EnumMembers!B] == [B.a, B.b, B.c, B.d, B.e]); + } + { + enum A : string { a = "alpha", b = "beta" } + static assert([EnumMembers!A] == [A.a, A.b]); + + static struct S + { + int value; + int opCmp(S rhs) const nothrow { return value - rhs.value; } + } + enum B : S { a = S(1), b = S(2), c = S(3) } + static assert([EnumMembers!B] == [B.a, B.b, B.c]); + } + { + enum A { a = 0, b = 0, c = 1, d = 1, e } + static assert([EnumMembers!A] == [A.a, A.b, A.c, A.d, A.e]); + } + { + enum E { member, a = 0, b = 0 } + + static assert(__traits(isSame, EnumMembers!E[0], E.member)); + static assert(__traits(isSame, EnumMembers!E[1], E.a)); + static assert(__traits(isSame, EnumMembers!E[2], E.b)); + + static assert(__traits(identifier, EnumMembers!E[0]) == "member"); + static assert(__traits(identifier, EnumMembers!E[1]) == "a"); + static assert(__traits(identifier, EnumMembers!E[2]) == "b"); + } +} + +// https://issues.dlang.org/show_bug.cgi?id=14561: huge enums +@safe unittest +{ + static string genEnum() + { + string result = "enum TLAs {"; + foreach (c0; '0' .. '2' + 1) + { + foreach (c1; '0' .. '9' + 1) + { + foreach (c2; '0' .. '9' + 1) + { + foreach (c3; '0' .. '9' + 1) + { + result ~= '_'; + result ~= c0; + result ~= c1; + result ~= c2; + result ~= c3; + result ~= ','; + } + } + } + } + result ~= '}'; + return result; + } + mixin(genEnum); + static assert(EnumMembers!TLAs[0] == TLAs._0000); + static assert(EnumMembers!TLAs[$ - 1] == TLAs._2999); +} + /++ Whether the type $(D From) is implicitly convertible to the type $(D To). @@ -1705,6 +1875,146 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is } } +/++ + Whether the given values are equal per $(D ==). + + All this does is $(D lhs == rhs) but in an eponymous template, so most code + shouldn't use it. It's intended to be used in conjunction with templates + that take a template predicate - such as those in phobos.sys.meta. + + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + + Note that in most cases, even when comparing values at compile time, using + isEqual makes no sense, because you can use CTFE to just compare two values + (or expressions which evaluate to values), but in rare cases where you need + to compare symbols in an $(D AliasSeq) by value with a template predicate + while still leaving them as symbols in an $(D AliasSeq), then isEqual would + be needed. + + A prime example of this would be $(D Unique!(isEqual, EnumMembers!MyEnum)), + which results in an $(D AliasSeq) containing the list of members of + $(D MyEnum) but without any duplicate values (e.g. to use when doing code + generation to create a final switch). + + Alternatively, code such as $(D [EnumMembers!MyEnum].sort().unique()) could + be used to get a dynamic array of the enum members with no duplicate values + via CTFE, thus avoiding the need for template predicates or anything from + phobos.sys.meta. However, you then have a dynamic array of enum values + rather than an $(D AliasSeq) of symbols for those enum members, which + affects what you can do with type introspection. So, which approach is + better depends on what the code needs to do with the enum members. + + In general, however, if code doesn't need an $(D AliasSeq), and an array of + values will do the trick, then it's more efficient to operate on an array of + values with CTFE and avoid using isEqual or other templates to operate on + the values as an $(D AliasSeq). + + See_Also: + $(LREF isSameSymbol) + $(LREF isSameType) + +/ +enum isEqual(alias lhs, alias rhs) = lhs == rhs; + +/++ Ditto +/ +template isEqual(alias lhs) +{ + enum isEqual(alias rhs) = lhs == rhs; +} + +/// It acts just like ==, but it's a template. +@safe unittest +{ + enum a = 42; + + static assert( isEqual!(a, 42)); + static assert( isEqual!(20, 10 + 10)); + + static assert(!isEqual!(a, 120)); + static assert(!isEqual!(77, 19 * 7 + 2)); + + // b cannot be read at compile time, so it won't work with isEqual. + int b = 99; + static assert(!__traits(compiles, isEqual!(b, 99))); +} + +/++ + Comparing some of the differences between an $(D AliasSeq) of enum members + and an array of enum values created from an $(D AliasSeq) of enum members. + +/ +@safe unittest +{ + import phobos.sys.meta : AliasSeq, Unique; + + enum E + { + a = 0, + b = 22, + c = 33, + d = 0, + e = 256, + f = 33, + g = 7 + } + + alias uniqueMembers = Unique!(isEqual, EnumMembers!E); + static assert(uniqueMembers.length == 5); + + static assert(__traits(isSame, uniqueMembers[0], E.a)); + static assert(__traits(isSame, uniqueMembers[1], E.b)); + static assert(__traits(isSame, uniqueMembers[2], E.c)); + static assert(__traits(isSame, uniqueMembers[3], E.e)); + static assert(__traits(isSame, uniqueMembers[4], E.g)); + + static assert(__traits(identifier, uniqueMembers[0]) == "a"); + static assert(__traits(identifier, uniqueMembers[1]) == "b"); + static assert(__traits(identifier, uniqueMembers[2]) == "c"); + static assert(__traits(identifier, uniqueMembers[3]) == "e"); + static assert(__traits(identifier, uniqueMembers[4]) == "g"); + + // Same value but different symbol. + static assert(uniqueMembers[0] == E.d); + static assert(!__traits(isSame, uniqueMembers[0], E.d)); + + // is expressions compare types, not symbols or values, and these AliasSeqs + // contain the list of symbols for the enum members, not types, so the is + // expression evaluates to false even though the symbols are the same. + static assert(!is(uniqueMembers == AliasSeq!(E.a, E.b, E.c, E.e, E.g))); + + // Once the members are converted to an array, the types are the same, and + // the values are the same, but the symbols are not the same. Instead of + // being the symbols E.a, E.b, etc., they're just values with the type E + // which match the values of E.a, E.b, etc. + enum arr = [uniqueMembers]; + static assert(is(typeof(arr) == E[])); + + static assert(arr == [E.a, E.b, E.c, E.e, E.g]); + static assert(arr == [E.d, E.b, E.f, E.e, E.g]); + + static assert(!__traits(isSame, arr[0], E.a)); + static assert(!__traits(isSame, arr[1], E.b)); + static assert(!__traits(isSame, arr[2], E.c)); + static assert(!__traits(isSame, arr[3], E.e)); + static assert(!__traits(isSame, arr[4], E.g)); + + // Since arr[0] is just a value of type E, it's no longer the symbol, E.a, + // even though its type is E, and its value is the same as that of E.a. And + // unlike the actual members of an enum, an element of an array does not + // have an identifier, so __traits(identifier, ...) doesn't work with it. + static assert(!__traits(compiles, __traits(identifier, arr[0]))); + + // Similarly, once an enum member from the AliasSeq is assigned to a + // variable, __traits(identifer, ...) operates on the variable, not the + // symbol from the AliasSeq or the value of the variable. + auto var = uniqueMembers[0]; + static assert(__traits(identifier, var) == "var"); + + // The same with a manifest constant. + enum constant = uniqueMembers[0]; + static assert(__traits(identifier, constant) == "constant"); +} + /++ Whether the given symbols are the same symbol. @@ -1718,6 +2028,7 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is See_Also: $(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, lhs, rhs))) + $(LREF isEqual) $(LREF isSameType) +/ enum isSameSymbol(alias lhs, alias rhs) = __traits(isSame, lhs, rhs); @@ -1798,6 +2109,7 @@ template isSameSymbol(alias lhs) template predicates. See_Also: + $(LREF isEqual) $(LREF isSameSymbol) +/ enum isSameType(T, U) = is(T == U); From aeace12de6f6a4e9ab96c7ed52b5f6fcd91e740f Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 7 Apr 2024 08:34:39 -0600 Subject: [PATCH 37/57] Add lvalueOf and rvalueOf to phobos.sys.traits. These are straight from std.traits but with improvements to the documentation and unit tests. The __InoutWorkaroundStruct bit is kind of ugly, but it does seem to be necessary due to how inout works. --- phobos/sys/traits.d | 99 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index cfc2ac27425..5619a49c3b7 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -68,9 +68,9 @@ $(LREF isQualifierConvertible) )) $(TR $(TD Traits for comparisons) $(TD - $(LREF isEqual) - $(LREF isSameSymbol) - $(LREF isSameType) + $(LREF isEqual) + $(LREF isSameSymbol) + $(LREF isSameType) )) $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) @@ -78,10 +78,14 @@ $(LREF Unqualified) )) $(TR $(TD Type Constructors) $(TD - $(LREF ConstOf) - $(LREF ImmutableOf) - $(LREF InoutOf) - $(LREF SharedOf) + $(LREF ConstOf) + $(LREF ImmutableOf) + $(LREF InoutOf) + $(LREF SharedOf) + )) + $(TR $(TD Misc) $(TD + $(LREF lvalueOf) + $(LREF rvalueOf) )) ) @@ -2658,3 +2662,84 @@ alias SharedOf(T) = shared T; shared(bool*), shared(ubyte[]), shared(string), immutable(string)))); } + +// Needed for rvalueOf/lvalueOf because +// "inout on return means inout must be on a parameter as well" +private struct __InoutWorkaroundStruct {} + +/++ + Creates an lvalue or rvalue of type T to be used in conjunction with + $(D is(typeof(...))) or + $(DDSUBLINK spec/traits, compiles, $(D __traits(compiles, ...))). + + The idea is that some traits or other forms of conditional compilation need + to verify that a particular piece of code compiles with an rvalue or an + lvalue of a specific type, and these $(D @property) functions allow you to + get an rvalue or lvalue of a specific type to use within an expression that + is then tested to see whether it compiles. + + They're $(D @property) functions so that using $(D typeof) on them gives + the return type rather than the type of the function. + + Note that these functions are $(I not) defined, so if they're actually used + outside of type introspection, they'll result in linker errors. They're + entirely for testing that a particular piece of code compiles with an rvalue + or lvalue of the given type. + + The $(D __InoutWorkaroundStruct) parameter is entirely to make it so that + these work when the given type has the $(D inout) qualifier, since the + language requires that a function that returns an $(D inout) type also have + an $(D inout) type as a parameter. It should just be ignored. + +/ +@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); + +/++ Ditto +/ +@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); + +/// +@safe unittest +{ + static int foo(int); + static assert(is(typeof(foo(lvalueOf!int)) == int)); + static assert(is(typeof(foo(rvalueOf!int)) == int)); + + static bool bar(ref int); + static assert(is(typeof(bar(lvalueOf!int)) == bool)); + static assert(!is(typeof(bar(rvalueOf!int)))); + + static assert( is(typeof({ lvalueOf!int = 42; }))); + static assert(!is(typeof({ rvalueOf!int = 42; }))); + + static struct S {} + static assert( is(typeof({ lvalueOf!S = S.init; }))); + static assert(!is(typeof({ rvalueOf!S = S.init; }))); + + static struct NoAssign + { + @disable void opAssign(ref NoAssign); + } + static assert(!is(typeof({ lvalueOf!NoAssign = NoAssign.init; }))); + static assert(!is(typeof({ rvalueOf!NoAssign = NoAssign.init; }))); +} + +@system unittest +{ + import phobos.sys.meta : AliasSeq; + + void needLvalue(T)(ref T); + static struct S {} + int i; + struct Nested { void f() { ++i; } } + + static foreach (T; AliasSeq!(int, const int, immutable int, inout int, string, S, Nested, Object)) + { + static assert(!__traits(compiles, needLvalue(rvalueOf!T))); + static assert( __traits(compiles, needLvalue(lvalueOf!T))); + static assert(is(typeof(rvalueOf!T) == T)); + static assert(is(typeof(lvalueOf!T) == T)); + } + + static assert(!__traits(compiles, rvalueOf!int = 1)); + static assert( __traits(compiles, lvalueOf!byte = 127)); + static assert(!__traits(compiles, lvalueOf!byte = 128)); +} From 95a9d9f1227866e526ebe4f6c2b6fedf578bc8d3 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 Apr 2024 07:12:50 -0600 Subject: [PATCH 38/57] Adds Instantiate, ApplyLeft, and ApplyRight to phobos.sys.meta. This also makes it so that isImplicitlyConvertible and isQualifierConvertible in phobos.sys.traits can be partially instantiated, since I missed that before. Instantiate is basically identical to the one in std.meta implementation-wise, but the documentation and unit tests have been improved. ApplyLeft and ApplyRight should be functionally the same as their std.meta counterparts, but they have a simpler implementation than their std.meta counterparts (I'm pretty sure that the extra complexity in the old implementation is to work around built-in types not working with aliases, which was fixed a while ago). Of course, their documentation and tests have also been improved. --- phobos/sys/meta.d | 329 ++++++++++++++++++++++++++++++++++++++++++++ phobos/sys/traits.d | 56 +++++++- 2 files changed, 384 insertions(+), 1 deletion(-) diff --git a/phobos/sys/meta.d b/phobos/sys/meta.d index e28a4d1c1ce..9abab19568d 100644 --- a/phobos/sys/meta.d +++ b/phobos/sys/meta.d @@ -67,6 +67,11 @@ $(LREF any) $(LREF indexOf) )) + $(TR $(TD Template instantiation) $(TD + $(LREF ApplyLeft) + $(LREF ApplyRight) + $(LREF Instantiate) + )) ) References: @@ -672,3 +677,327 @@ unittest static assert(indexOf!(isSameType!string, int, int, int, string) == 3); static assert(indexOf!(isSameType!string, int, int, int, int) == -1); } + +/++ + Instantiates the given template with the given arguments and evaluates to + the result of that template. + + This is used to work around some syntactic limitations that D has with + regards to instantiating templates. Essentially, D requires a name for a + template when instantiating it (be it the name of the template itself or an + alias to the template), which causes problems when you don't have that. + + Specifically, if the template is within an $(LREF AliasSeq) - e.g. + $(D Templates[0]!Args) - or it's the result of another template - e.g + $(D Foo!Bar!Baz) - the instantiation is illegal. This leaves two ways to + solve the problem. The first is to create an alias, e.g. + --- + alias Template = Templates[0]; + enum result = Template!Args; + + alias Partial = Foo!Bar; + alias T = Partial!Baz; + --- + The second is to use Instantiate, e.g. + --- + enum result = Instantiate!(Templates[0], Args); + + alias T = Instiantiate!(Foo!Bar, Baz); + --- + + Of course, the downside to this is that it adds an additional template + instantiation, but it avoids creating an alias just to be able to + instantiate a template. So, whether it makes sense to use Instantiate + instead of an alias naturally depends on the situation, but without it, + we'd be forced to create aliases even in situations where that's + problematic. + + See_Also: + $(LREF ApplyLeft) + $(LREF ApplyRight) + +/ +alias Instantiate(alias Template, Args...) = Template!Args; + +/// +@safe unittest +{ + import phobos.sys.traits : ConstOf, isImplicitlyConvertible, isSameType, isInteger; + + alias Templates = AliasSeq!(isImplicitlyConvertible!int, + isSameType!string, + isInteger, + ConstOf); + + // Templates[0]!long does not compile, because the compiler can't parse it. + + static assert( Instantiate!(Templates[0], long)); + static assert(!Instantiate!(Templates[0], string)); + + static assert(!Instantiate!(Templates[1], long)); + static assert( Instantiate!(Templates[1], string)); + + static assert( Instantiate!(Templates[2], long)); + static assert(!Instantiate!(Templates[2], string)); + + static assert(is(Instantiate!(Templates[3], int) == const int)); + static assert(is(Instantiate!(Templates[3], double) == const double)); +} + +/// +@safe unittest +{ + template hasMember(string member) + { + enum hasMember(T) = __traits(hasMember, T, member); + } + + struct S + { + int foo; + } + + // hasMember!"foo"!S does not compile, + // because having multiple ! arguments is not allowed. + + static assert( Instantiate!(hasMember!"foo", S)); + static assert(!Instantiate!(hasMember!"bar", S)); +} + +/++ + Instantiate also allows us to do template instantations via templates that + take other templates as arguments. + +/ +@safe unittest +{ + import phobos.sys.traits : isInteger, isNumeric, isUnsignedInteger; + + alias Results = Map!(ApplyRight!(Instantiate, int), + isInteger, isNumeric, isUnsignedInteger); + + static assert([Results] == [true, true, false]); +} + +/++ + ApplyLeft does a + $(LINK2 http://en.wikipedia.org/wiki/Partial_application, partial application) + of its arguments, providing a way to bind a set of arguments to the given + template while delaying actually instantiating that template until the full + set of arguments is provided. The "Left" in the name indicates that the + initial arguments are one the left-hand side of the argument list + when the given template is instantiated. + + Essentially, ApplyLeft results in a template that stores Template and Args, + and when that intermediate template is instantiated in turn, it instantiates + Template with Args on the left-hand side of the arguments to Template and + with the arguments to the intermediate template on the right-hand side - + i.e. Args is applied to the left when instantiating Template. + + So, if you have + --- + alias Intermediate = ApplyLeft!(MyTemplate, Arg1, Arg2); + alias Result = Intermediate!(ArgA, ArgB); + --- + then that is equivalent to + --- + alias Result = MyTemplate!(Arg1, Arg2, ArgA, ArgB); + --- + with the difference being that you have an intermediate template which can + be stored or passed to other templates (e.g. as a template predicate). + + The only difference between ApplyLeft and $(LREF ApplyRight) is whether + Args is on the left-hand or the right-hand side of the arguments given to + Template when it's instantiated. + + Note that in many cases, the need for ApplyLeft can be eliminated by making + it so that Template can be partially instantiated. E.G. + --- + enum isSameType(T, U) = is(T == U); + + template isSameType(T) + { + enum isSameType(U) = is(T == U); + } + --- + makes it so that both of these work + --- + enum result1 = isSameType!(int, long); + + alias Intermediate = isSameType!int; + enum result2 = Intermediate!long; + --- + whereas if only the two argument version is provided, then ApplyLeft would + be required for the second use case. + --- + enum result1 = isSameType!(int, long); + + alias Intermediate = ApplyLeft!(isSameType, int); + enum result2 = Intermediate!long; + --- + + See_Also: + $(LREF ApplyRight) + $(LREF Instantiate) + +/ +template ApplyLeft(alias Template, Args...) +{ + alias ApplyLeft(Right...) = Template!(Args, Right); +} + +/// +@safe unittest +{ + { + alias Intermediate = ApplyLeft!(AliasSeq, ubyte, ushort, uint); + alias Result = Intermediate!(char, wchar, dchar); + static assert(is(Result == AliasSeq!(ubyte, ushort, uint, char, wchar, dchar))); + } + { + enum isImplicitlyConvertible(T, U) = is(T : U); + + // i.e. isImplicitlyConvertible!(ubyte, T) is what all is checking for + // with each element in the AliasSeq. + static assert(all!(ApplyLeft!(isImplicitlyConvertible, ubyte), + short, ushort, int, uint, long, ulong)); + } + { + enum hasMember(T, string member) = __traits(hasMember, T, member); + + struct S + { + bool foo; + int bar; + string baz; + } + + static assert(all!(ApplyLeft!(hasMember, S), "foo", "bar", "baz")); + } + { + // Either set of arguments can be empty, since the first set is just + // stored to be applied later, and then when the intermediate template + // is instantiated, they're all applied to the given template in the + // requested order. However, whether the code compiles when + // instantiating the intermediate template depends on what kinds of + // arguments the given template requires. + + alias Intermediate1 = ApplyLeft!AliasSeq; + static assert(Intermediate1!().length == 0); + + enum isSameSize(T, U) = T.sizeof == U.sizeof; + + alias Intermediate2 = ApplyLeft!(isSameSize, int); + static assert(Intermediate2!uint); + + alias Intermediate3 = ApplyLeft!(isSameSize, int, uint); + static assert(Intermediate3!()); + + alias Intermediate4 = ApplyLeft!(isSameSize); + static assert(Intermediate4!(int, uint)); + + // isSameSize requires two arguments + alias Intermediate5 = ApplyLeft!isSameSize; + static assert(!__traits(compiles, Intermediate5!())); + static assert(!__traits(compiles, Intermediate5!int)); + static assert(!__traits(compiles, Intermediate5!(int, long, string))); + } +} + +/++ + ApplyRight does a + $(LINK2 http://en.wikipedia.org/wiki/Partial_application, partial application) + of its arguments, providing a way to bind a set of arguments to the given + template while delaying actually instantiating that template until the full + set of arguments is provided. The "Right" in the name indicates that the + initial arguments are one the right-hand side of the argument list + when the given template is instantiated. + + Essentially, ApplyRight results in a template that stores Template and + Args, and when that intermediate template is instantiated in turn, it + instantiates Template with the arguments to the intermediate template on + the left-hand side and with Args on the right-hand side - i.e. Args is + applied to the right when instantiating Template. + + So, if you have + --- + alias Intermediate = ApplyRight!(MyTemplate, Arg1, Arg2); + alias Result = Intermediate!(ArgA, ArgB); + --- + then that is equivalent to + --- + alias Result = MyTemplate!(ArgA, ArgB, Arg1, Arg2); + --- + with the difference being that you have an intermediate template which can + be stored or passed to other templates (e.g. as a template predicate). + + The only difference between $(LREF ApplyLeft) and ApplyRight is whether + Args is on the left-hand or the right-hand side of the arguments given to + Template when it's instantiated. + + See_Also: + $(LREF ApplyLeft) + $(LREF Instantiate) + +/ +template ApplyRight(alias Template, Args...) +{ + alias ApplyRight(Left...) = Template!(Left, Args); +} + +/// +@safe unittest +{ + { + alias Intermediate = ApplyRight!(AliasSeq, ubyte, ushort, uint); + alias Result = Intermediate!(char, wchar, dchar); + static assert(is(Result == AliasSeq!(char, wchar, dchar, ubyte, ushort, uint))); + } + { + enum isImplicitlyConvertible(T, U) = is(T : U); + + // i.e. isImplicitlyConvertible!(T, short) is what Filter is checking + // for with each element in the AliasSeq. + static assert(is(Filter!(ApplyRight!(isImplicitlyConvertible, short), + ubyte, string, short, float, int) == + AliasSeq!(ubyte, short))); + } + { + enum hasMember(T, string member) = __traits(hasMember, T, member); + + struct S1 + { + bool foo; + } + + struct S2 + { + int foo() { return 42; } + } + + static assert(all!(ApplyRight!(hasMember, "foo"), S1, S2)); + } + { + // Either set of arguments can be empty, since the first set is just + // stored to be applied later, and then when the intermediate template + // is instantiated, they're all applied to the given template in the + // requested order. However, whether the code compiles when + // instantiating the intermediate template depends on what kinds of + // arguments the given template requires. + + alias Intermediate1 = ApplyRight!AliasSeq; + static assert(Intermediate1!().length == 0); + + enum isSameSize(T, U) = T.sizeof == U.sizeof; + + alias Intermediate2 = ApplyRight!(isSameSize, int); + static assert(Intermediate2!uint); + + alias Intermediate3 = ApplyRight!(isSameSize, int, uint); + static assert(Intermediate3!()); + + alias Intermediate4 = ApplyRight!(isSameSize); + static assert(Intermediate4!(int, uint)); + + // isSameSize requires two arguments + alias Intermediate5 = ApplyRight!isSameSize; + static assert(!__traits(compiles, Intermediate5!())); + static assert(!__traits(compiles, Intermediate5!int)); + } +} diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 5619a49c3b7..828ac36dbfc 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -1496,6 +1496,10 @@ if (is(E == enum)) used in conjunction with templates that use a template predicate (such as many of the templates in phobos.sys.meta). + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + See_Also: $(DDSUBLINK dlang.org/spec/type, implicit-conversions, Spec on implicit conversions) $(DDSUBLINK spec/const3, implicit_qualifier_conversions, Spec for implicit qualifier conversions) @@ -1503,6 +1507,12 @@ if (is(E == enum)) +/ enum isImplicitlyConvertible(From, To) = is(From : To); +/++ Ditto +/ +template isImplicitlyConvertible(From) +{ + enum isImplicitlyConvertible(To) = is(From : To); +} + /// @safe unittest { @@ -1706,6 +1716,23 @@ enum isImplicitlyConvertible(From, To) = is(From : To); static assert(!isImplicitlyConvertible!(const Base[], immutable Base[])); } +/++ + isImplicitlyConvertible can be used with partial instantiation so that it + can be passed to a template which takes a unary predicate. + +/ +@safe unittest +{ + import phobos.sys.meta : AliasSeq, all, indexOf; + + // byte is implicitly convertible to byte, short, int, and long. + static assert(all!(isImplicitlyConvertible!byte, short, int, long)); + + // const(char)[] at index 2 is the first type in the AliasSeq which string + // can be implicitly converted to. + alias Types = AliasSeq!(int, char[], const(char)[], string, int*); + static assert(indexOf!(isImplicitlyConvertible!string, Types) == 2); +} + /++ Whether $(D From) is $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) @@ -1750,12 +1777,22 @@ enum isImplicitlyConvertible(From, To) = is(From : To); to $(D const char) rather than just $(D char), $(D const char), and $(D immutable char). + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + See_Also: $(DDSUBLINK spec/const3, implicit_qualifier_conversions, Spec for implicit qualifier conversions) $(LREF isImplicitlyConvertible) +/ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is(From* : To*); +/++ Ditto +/ +template isQualifierConvertible(From) +{ + enum isQualifierConvertible(To) = is(immutable From == immutable To) && is(From* : To*); +} + /// @safe unittest { @@ -1794,7 +1831,7 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is static assert( isQualifierConvertible!(inout int, const int)); static assert(!isQualifierConvertible!(inout int, immutable int)); - // shared is of course also a qualifer. + // shared is of course also a qualifier. static assert(!isQualifierConvertible!(int, shared int)); static assert(!isQualifierConvertible!(int, const shared int)); static assert(!isQualifierConvertible!(const int, shared int)); @@ -1846,6 +1883,23 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is static assert(!isQualifierConvertible!(string, const(ubyte)[])); } +/++ + isQualifierConvertible can be used with partial instantiation so that it + can be passed to a template which takes a unary predicate. + +/ +@safe unittest +{ + import phobos.sys.meta : AliasSeq, all, indexOf; + + // byte is qualifier convertible to byte and const byte. + static assert(all!(isQualifierConvertible!byte, byte, const byte)); + + // const(char[]) at index 2 is the first type in the AliasSeq which string + // is qualifier convertible to. + alias Types = AliasSeq!(int, char[], const(char[]), string, int*); + static assert(indexOf!(isQualifierConvertible!string, Types) == 2); +} + @safe unittest { import phobos.sys.meta : AliasSeq; From 0663564600edb3cce6e0925599ebe8a6da8c20fd Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Tue, 9 Apr 2024 04:20:12 -0600 Subject: [PATCH 39/57] Add And, Not, and Or to phobos.sys.meta. (#8977) These correspond to templateAnd, templateNot, and templateOr from std.meta. The names have been shortened and made PascalCase to make them consistent with our normal naming rules. It's PascalCase rather than camelCase, because the result is a template rather than a value, and we don't normally prefix symbols with "template" just because they're templates. The implementation of Not is unchanged. However, unlike their std.meta counterparts, And and Or do not short-circuit their evaluation. This makes them consistent with the rest of phobos.sys.meta (whereas std.meta is inconsistent about whether its templates short-circuit their evaluation), but it's also because they're implemented to be iterative rather than recursive, which should make them more efficient. --- phobos/sys/meta.d | 256 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 254 insertions(+), 2 deletions(-) diff --git a/phobos/sys/meta.d b/phobos/sys/meta.d index 9abab19568d..1c31b67a72f 100644 --- a/phobos/sys/meta.d +++ b/phobos/sys/meta.d @@ -50,8 +50,8 @@ $(BOOKTABLE , $(TR $(TH Category) $(TH Templates)) $(TR $(TD Building blocks) $(TD - $(LREF Alias) - $(LREF AliasSeq) + $(LREF Alias) + $(LREF AliasSeq) )) $(TR $(TD Alias sequence filtering) $(TD $(LREF Filter) @@ -67,6 +67,11 @@ $(LREF any) $(LREF indexOf) )) + $(TR $(TD Template predicates) $(TD + $(LREF And) + $(LREF Not) + $(LREF Or) + )) $(TR $(TD Template instantiation) $(TD $(LREF ApplyLeft) $(LREF ApplyRight) @@ -678,6 +683,253 @@ unittest static assert(indexOf!(isSameType!string, int, int, int, int) == -1); } +/++ + Combines multiple template predicates into a single template predicate using + logical AND - i.e. for the resulting predicate to be $(D true) with a + particular argument, all of the predicates must be $(D true) with that + argument. + + Evaluation is $(I not) short-circuited if a $(D false) result is + encountered; the template predicate must be instantiable with all the + elements. + + See_Also: + $(LREF Not) + $(LREF Or) + +/ +template And(Preds...) +{ + enum And(Args...) = + { + static foreach (Pred; Preds) + { + static if (!Pred!Args) + return false; + } + return true; + }(); +} + +/// +@safe unittest +{ + import phobos.sys.traits : isNumeric; + + template isSameSize(size_t size) + { + enum isSameSize(T) = T.sizeof == size; + } + + alias is32BitNumeric = And!(isNumeric, isSameSize!4); + + static assert(!is32BitNumeric!short); + static assert( is32BitNumeric!int); + static assert(!is32BitNumeric!long); + static assert( is32BitNumeric!float); + static assert(!is32BitNumeric!double); + static assert(!is32BitNumeric!(int*)); + + // An empty sequence of predicates always yields true. + alias alwaysTrue = And!(); + static assert(alwaysTrue!int); +} + +/++ + Predicates with multiple parameters are also supported. However, the number + of parameters must match. + +/ +@safe unittest +{ + import phobos.sys.traits : isImplicitlyConvertible, isInteger, isSameType; + + alias isOnlyImplicitlyConvertible + = And!(Not!isSameType, isImplicitlyConvertible); + + static assert( isOnlyImplicitlyConvertible!(int, long)); + static assert(!isOnlyImplicitlyConvertible!(int, int)); + static assert(!isOnlyImplicitlyConvertible!(long, int)); + + static assert( isOnlyImplicitlyConvertible!(string, const(char)[])); + static assert(!isOnlyImplicitlyConvertible!(string, string)); + static assert(!isOnlyImplicitlyConvertible!(const(char)[], string)); + + // Mismatched numbers of parameters. + alias doesNotWork = And!(isInteger, isImplicitlyConvertible); + static assert(!__traits(compiles, doesNotWork!int)); + static assert(!__traits(compiles, doesNotWork!(int, long))); +} + +@safe unittest +{ + enum testAlways(Args...) = true; + enum testNever(Args...) = false; + + static assert( Instantiate!(And!(testAlways, testAlways, testAlways), int)); + static assert(!Instantiate!(And!(testAlways, testAlways, testNever), int)); + static assert(!Instantiate!(And!(testAlways, testNever, testNever), int)); + static assert(!Instantiate!(And!(testNever, testNever, testNever), int)); + static assert(!Instantiate!(And!(testNever, testNever, testAlways), int)); + static assert(!Instantiate!(And!(testNever, testAlways, testAlways), int)); + + static assert( Instantiate!(And!(testAlways, testAlways), int)); + static assert(!Instantiate!(And!(testAlways, testNever), int)); + static assert(!Instantiate!(And!(testNever, testAlways), int)); + static assert(!Instantiate!(And!(testNever, testNever), int)); + + static assert( Instantiate!(And!testAlways, int)); + static assert(!Instantiate!(And!testNever, int)); + + // No short-circuiting. + import phobos.sys.traits : isEqual, isFloatingPoint; + static assert(!Instantiate!(And!isFloatingPoint, int)); + static assert(!__traits(compiles, Instantiate!(And!(isFloatingPoint, isEqual), int))); +} + +/++ + Evaluates to a template predicate which negates the given predicate. + + See_Also: + $(LREF And) + $(LREF Or) + +/ +template Not(alias Pred) +{ + enum Not(Args...) = !Pred!Args; +} + +/// +@safe unittest +{ + import phobos.sys.traits : isDynamicArray, isPointer; + + alias isNotPointer = Not!isPointer; + static assert( isNotPointer!int); + static assert(!isNotPointer!(int*)); + static assert( all!(isNotPointer, string, char, float)); + + static assert(!all!(Not!isDynamicArray, string, char[], int[], long)); + static assert( any!(Not!isDynamicArray, string, char[], int[], long)); +} + +/++ + Predicates with multiple parameters are also supported. + +/ +@safe unittest +{ + import phobos.sys.traits : isImplicitlyConvertible, isInteger; + + alias notImplicitlyConvertible = Not!isImplicitlyConvertible; + + static assert( notImplicitlyConvertible!(long, int)); + static assert(!notImplicitlyConvertible!(int, long)); + + static assert( notImplicitlyConvertible!(const(char)[], string)); + static assert(!notImplicitlyConvertible!(string, const(char)[])); +} + +/++ + Combines multiple template predicates into a single template predicate using + logical OR - i.e. for the resulting predicate to be $(D true) with a + particular argument, at least one of the predicates must be $(D true) with + that argument. + + Evaluation is $(I not) short-circuited if a $(D true) result is + encountered; the template predicate must be instantiable with all the + elements. + + See_Also: + $(LREF And) + $(LREF Not) + +/ +template Or(Preds...) +{ + enum Or(Args...) = + { + static foreach (Pred; Preds) + { + static if (Pred!Args) + return true; + } + return false; + }(); +} + +/// +@safe unittest +{ + import phobos.sys.traits : isFloatingPoint, isSignedInteger; + + alias isSignedNumeric = Or!(isFloatingPoint, isSignedInteger); + + static assert( isSignedNumeric!short); + static assert( isSignedNumeric!long); + static assert( isSignedNumeric!double); + static assert(!isSignedNumeric!uint); + static assert(!isSignedNumeric!ulong); + static assert(!isSignedNumeric!string); + static assert(!isSignedNumeric!(int*)); + + // An empty sequence of predicates always yields false. + alias alwaysFalse = Or!(); + static assert(!alwaysFalse!int); +} + +/++ + Predicates with multiple parameters are also supported. However, the number + of parameters must match. + +/ +@safe unittest +{ + import phobos.sys.traits : isImplicitlyConvertible, isInteger; + + enum isSameSize(T, U) = T.sizeof == U.sizeof; + alias convertibleOrSameSize = Or!(isImplicitlyConvertible, isSameSize); + + static assert( convertibleOrSameSize!(int, int)); + static assert( convertibleOrSameSize!(int, long)); + static assert(!convertibleOrSameSize!(long, int)); + + static assert( convertibleOrSameSize!(int, float)); + static assert( convertibleOrSameSize!(float, int)); + static assert(!convertibleOrSameSize!(double, int)); + static assert(!convertibleOrSameSize!(float, long)); + + static assert( convertibleOrSameSize!(int*, string*)); + + // Mismatched numbers of parameters. + alias doesNotWork = Or!(isInteger, isImplicitlyConvertible); + static assert(!__traits(compiles, doesNotWork!int)); + static assert(!__traits(compiles, doesNotWork!(int, long))); +} + +@safe unittest +{ + enum testAlways(Args...) = true; + enum testNever(Args...) = false; + + static assert( Instantiate!(Or!(testAlways, testAlways, testAlways), int)); + static assert( Instantiate!(Or!(testAlways, testAlways, testNever), int)); + static assert( Instantiate!(Or!(testAlways, testNever, testNever), int)); + static assert(!Instantiate!(Or!(testNever, testNever, testNever), int)); + + static assert( Instantiate!(Or!(testAlways, testAlways), int)); + static assert( Instantiate!(Or!(testAlways, testNever), int)); + static assert( Instantiate!(Or!(testNever, testAlways), int)); + static assert(!Instantiate!(Or!(testNever, testNever), int)); + + static assert( Instantiate!(Or!testAlways, int)); + static assert(!Instantiate!(Or!testNever, int)); + + static assert(Instantiate!(Or!testAlways, int)); + static assert(Instantiate!(Or!testAlways, Map)); + static assert(Instantiate!(Or!testAlways, int, Map)); + + // No short-circuiting. + import phobos.sys.traits : isEqual, isInteger; + static assert( Instantiate!(Or!isInteger, int)); + static assert(!__traits(compiles, Instantiate!(Or!(isInteger, isEqual), int))); +} + /++ Instantiates the given template with the given arguments and evaluates to the result of that template. From c489361e71e7d1075f07a855316a7acdb95687ee Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 12 Apr 2024 18:09:58 -0600 Subject: [PATCH 40/57] Add OriginalType, KeyType, and ValueType to phobos.sys.traits. (#8978) They're each the same as their std.traits counterparts, though the documentation and tests have been updated. I considered renaming KeyType and ValueType to AAKeyType and AAValueType for increased clarity, but that seems pretty ugly, and while "value type" could mean other things depending on the context, I don't know what else it could mean in this context. So, I left the names the same. --- phobos/sys/traits.d | 144 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 828ac36dbfc..23f16667e82 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -72,6 +72,11 @@ $(LREF isSameSymbol) $(LREF isSameType) )) + $(TR $(TD General Types) $(TD + $(LREF KeyType) + $(LREF OriginalType) + $(LREF ValueType) + )) $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) $(LREF Unshared) @@ -2201,6 +2206,145 @@ template isSameType(T) static assert(indexOf!(isSameType!int, Types) == 2); } +/++ + Takes a type which is an associative array and evaluates to the type of the + keys in that associative array. + + See_Also: + $(LREF ValueType) + +/ +alias KeyType(V : V[K], K) = K; + +/// +@safe unittest +{ + static assert(is(KeyType!(int[string]) == string)); + static assert(is(KeyType!(string[int]) == int)); + + static assert(is(KeyType!(string[const int]) == const int)); + static assert(is(KeyType!(const int[string]) == string)); + + struct S + { + int i; + } + + string[S] aa1; + static assert(is(KeyType!(typeof(aa1)) == S)); + + S[string] aa2; + static assert(is(KeyType!(typeof(aa2)) == string)); + + KeyType!(typeof(aa1)) key1 = S(42); + KeyType!(typeof(aa2)) key2 = "foo"; + + // Key types with indirections have their inner layers treated as const + // by the compiler, because the values of keys can't change, or the hash + // value could change, putting the associative array in an invalid state. + static assert(is(KeyType!(bool[string[]]) == const(string)[])); + static assert(is(KeyType!(bool[int*]) == const(int)*)); + + // If the given type is not an AA, then KeyType won't compile. + static assert(!__traits(compiles, KeyType!int)); + static assert(!__traits(compiles, KeyType!(int[]))); +} + +/++ + Takes a type which is an associative array and evaluates to the type of the + values in that associative array. + + See_Also: + $(LREF KeyType) + +/ +alias ValueType(V : V[K], K) = V; + +/// +@safe unittest +{ + static assert(is(ValueType!(int[string]) == int)); + static assert(is(ValueType!(string[int]) == string)); + + static assert(is(ValueType!(string[const int]) == string)); + static assert(is(ValueType!(const int[string]) == const int)); + + struct S + { + int i; + } + + string[S] aa1; + static assert(is(ValueType!(typeof(aa1)) == string)); + + S[string] aa2; + static assert(is(ValueType!(typeof(aa2)) == S)); + + ValueType!(typeof(aa1)) value1 = "foo"; + ValueType!(typeof(aa2)) value2 = S(42); + + // If the given type is not an AA, then ValueType won't compile. + static assert(!__traits(compiles, ValueType!int)); + static assert(!__traits(compiles, ValueType!(int[]))); +} + +/++ + Evaluates to the original / ultimate base type of an enum type - or for + non-enum types, it evaluates to the type that it's given. + + If the base type of the given enum type is not an enum, then the result of + OriginalType is its direct base type. However, if the base type of the + given enum is also an enum, then OriginalType gives the ultimate base type + - that is, it keeps getting the base type for each succesive enum in the + chain until it gets to a base type that isn't an enum, and that's the + result. So, the result will never be an enum type. + + If the given type has any qualifiers, the result will have those same + qualifiers. + +/ +version (StdDdoc) template OriginalType(T) +{ + import core.internal.traits : CoreOriginalType = OriginalType; + alias OriginalType = CoreOriginalType!T; +} +else +{ + import core.internal.traits : CoreOriginalType = OriginalType; + alias OriginalType = CoreOriginalType; +} + +/// +@safe unittest +{ + enum E { a, b, c } + static assert(is(OriginalType!E == int)); + + enum F : E { x = E.a } + static assert(is(OriginalType!F == int)); + + enum G : F { y = F.x } + static assert(is(OriginalType!G == int)); + static assert(is(OriginalType!(const G) == const int)); + static assert(is(OriginalType!(immutable G) == immutable int)); + static assert(is(OriginalType!(shared G) == shared int)); + + enum C : char { a = 'a', b = 'b' } + static assert(is(OriginalType!C == char)); + + enum D : string { d = "dlang" } + static assert(is(OriginalType!D == string)); + + static assert(is(OriginalType!int == int)); + static assert(is(OriginalType!(const long) == const long)); + static assert(is(OriginalType!string == string)); + + // OriginalType gets the base type of enums and for all other types gives + // the same type back. It does nothing special for other types - like + // classes - where one could talk about the type having a base type. + class Base {} + class Derived : Base {} + static assert(is(OriginalType!Base == Base)); + static assert(is(OriginalType!Derived == Derived)); +} + /++ Removes the outer layer of $(D const), $(D inout), or $(D immutable) from type $(D T). From 655a1e4d1fa9b2f5d66d7e5c8512633bbd96fa7c Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Sun, 14 Apr 2024 14:35:33 +0100 Subject: [PATCH 41/57] Fix ref foreach range variable See https://github.com/dlang/dmd/pull/16381. --- std/path.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/path.d b/std/path.d index 2dbdf418f33..a45865a9f18 100644 --- a/std/path.d +++ b/std/path.d @@ -3397,7 +3397,7 @@ do else { C[] pattmp; - foreach (ref pi; 0 .. pattern.length) + for (size_t pi = 0; pi < pattern.length; pi++) { const pc = pattern[pi]; switch (pc) From 80235ea0568caaa8993e2d4fc6e3b7daa102bd6c Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Wed, 17 Apr 2024 10:59:07 +0200 Subject: [PATCH 42/57] Add `@nogc nothrow` to randomUUID --- std/uuid.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/uuid.d b/std/uuid.d index dec2a1c276d..b8cac6ecdd7 100644 --- a/std/uuid.d +++ b/std/uuid.d @@ -1207,7 +1207,7 @@ public struct UUID * randomGen = uniform RNG * See_Also: $(REF isUniformRNG, std,random) */ -@safe UUID randomUUID() +@nogc nothrow @safe UUID randomUUID() { import std.random : rndGen; // A PRNG with fewer than `n` bytes of state cannot produce From 7740a857f3e0cba3e4d675e0efc73fc8b881cc19 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 17 Apr 2024 09:39:26 +0100 Subject: [PATCH 43/57] [std/net/isemail] Fix foreach ref index See https://github.com/dlang/dmd/pull/16381. --- std/net/isemail.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/std/net/isemail.d b/std/net/isemail.d index 12a29fe44c9..2234f3590ca 100644 --- a/std/net/isemail.d +++ b/std/net/isemail.d @@ -111,8 +111,9 @@ if (isSomeChar!(Char)) auto endOrDie = false; auto crlfCount = int.min; // int.min == not defined - foreach (ref i, e ; email) + for (size_t i; i < email.length; i++) { + auto e = email[i]; token = email.get(i, e); switch (context) From 5d6eacc0c46594d292b857b9a4ee6b4e339c564c Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 18 Apr 2024 16:44:31 -0600 Subject: [PATCH 44/57] Add isAggegregateType and isInstantiationOf to phobos.sys.traits. (#8981) isAggregateType is the same as isAggregateType from std.traits but with tweaked documentation and examples. isInstantiationOf is the equivalent of std.traits' isInstanceOf. The documentation and tests have been updated, and an overload for partial instantiation has been added. The reason for the name change is that "instance" is not normally used with templates (instantiation is typically considered to be the correct term). Rather, instance is normally used to indicate that a value is an instance of a particular type. So, using isInstanceOf to check whether a type is an instantiation of a particular template seems like a misuse of the term and like it could easily cause confusion. The downside of course is that the new name is longer and harder to type, but while it's a trait that is necessary in some situations, IMHO, it's not needed frequently enough for the longer name to be a problem - particularly when it's a clearer name. I did try to simplify isInstantationOf's implementation so that it didn't need an alias overload, but I failed, because apparently, when typeof is used on the instantiation of a function template, the fact that it's a template instantation is lost. So, unfortunately, we're forced to operate on the function's symbol rather than its type to detect whether it's an instantation of a particular template. The documentation has been updated to include that information. I also tried to then make the alias overload not need a helper template so that fewer template instantiations would be needed, but that didn't work either, because the alias overload needs a template specialization to work, and I couldn't find a way to write an is expression that would have the same effect. So maybe, someone can improve the implementation later if they can figure that out, but since it's the same implementation as std.traits, we're not any worse off. And the overload which operates on aggregate types probably sees a lot more use anyway. --- phobos/sys/traits.d | 262 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 23f16667e82..65d56af141b 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -51,8 +51,10 @@ $(BOOKTABLE , $(TR $(TH Category) $(TH Templates)) $(TR $(TD Categories of types) $(TD + $(LREF isAggregateType) $(LREF isDynamicArray) $(LREF isFloatingPoint) + $(LREF isInstantiationOf) $(LREF isInteger) $(LREF isNumeric) $(LREF isPointer) @@ -109,6 +111,33 @@ +/ module phobos.sys.traits; +/++ + Whether the given type is an "aggregate type" - i.e. a struct, class, + interface, or union. + +/ +enum isAggregateType(T) = is(T == struct) || is(T == class) || is(T == interface) || is(T == union); + +@safe unittest +{ + struct S {} + class C {} + interface I {} + union U {} + + static assert( isAggregateType!S); + static assert( isAggregateType!C); + static assert( isAggregateType!I); + static assert( isAggregateType!U); + static assert( isAggregateType!(const S)); + static assert( isAggregateType!(shared C)); + + static assert(!isAggregateType!int); + static assert(!isAggregateType!string); + static assert(!isAggregateType!(S*)); + static assert(!isAggregateType!(C[])); + static assert(!isAggregateType!(I[string])); +} + /++ Whether the given type is a dynamic array (or what is sometimes referred to as a slice, since a dynamic array in D is a slice of memory). @@ -1309,6 +1338,239 @@ enum isPointer(T) = is(T == U*, U); } } +/++ + Evaluates to $(D true) if the given type or symbol is an instantiation of + the given template. + + The overload which takes $(D T) operates on types and indicates whether an + aggregate type (i.e. struct, class, interface, or union) is an + instantiation of the given template. + + The overload which takes $(D Symbol) operates on function templates, + because unlike with aggregate types, the type of a function does not retain + the fact that it was instantiated from a template. So, for functions, it's + necessary to pass the function itself as a symbol rather than pass the type + of the function. + + The overload which takes $(D Symbol) also works with templates which are + not types or functions. + + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + +/ +template isInstantiationOf(alias Template, T) +if (__traits(isTemplate, Template)) +{ + enum isInstantiationOf = is(T == Template!Args, Args...); +} + +/++ Ditto +/ +template isInstantiationOf(alias Template, alias Symbol) +if (__traits(isTemplate, Template)) +{ + enum impl(alias T : Template!Args, Args...) = true; + enum impl(alias T) = false; + enum isInstantiationOf = impl!Symbol; +} + +/++ Ditto +/ +template isInstantiationOf(alias Template) +if (__traits(isTemplate, Template)) +{ + enum isInstantiationOf(T) = is(T == Template!Args, Args...); + + template isInstantiationOf(alias Symbol) + { + enum impl(alias T : Template!Args, Args...) = true; + enum impl(alias T) = false; + enum isInstantiationOf = impl!Symbol; + } +} + +/// Examples of templated types. +@safe unittest +{ + static struct S(T) {} + static class C(T) {} + + static assert( isInstantiationOf!(S, S!int)); + static assert( isInstantiationOf!(S, S!int)); + static assert( isInstantiationOf!(S, S!string)); + static assert( isInstantiationOf!(S, const S!string)); + static assert( isInstantiationOf!(S, shared S!string)); + static assert(!isInstantiationOf!(S, int)); + static assert(!isInstantiationOf!(S, C!int)); + static assert(!isInstantiationOf!(S, C!string)); + static assert(!isInstantiationOf!(S, C!(S!int))); + + static assert( isInstantiationOf!(C, C!int)); + static assert( isInstantiationOf!(C, C!string)); + static assert( isInstantiationOf!(C, const C!string)); + static assert( isInstantiationOf!(C, shared C!string)); + static assert(!isInstantiationOf!(C, int)); + static assert(!isInstantiationOf!(C, S!int)); + static assert(!isInstantiationOf!(C, S!string)); + static assert(!isInstantiationOf!(C, S!(C!int))); + + static struct Variadic(T...) {} + + static assert( isInstantiationOf!(Variadic, Variadic!())); + static assert( isInstantiationOf!(Variadic, Variadic!int)); + static assert( isInstantiationOf!(Variadic, Variadic!(int, string))); + static assert( isInstantiationOf!(Variadic, Variadic!(int, string, int))); + static assert( isInstantiationOf!(Variadic, const Variadic!(int, short))); + static assert( isInstantiationOf!(Variadic, shared Variadic!(int, short))); + static assert(!isInstantiationOf!(Variadic, int)); + static assert(!isInstantiationOf!(Variadic, S!int)); + static assert(!isInstantiationOf!(Variadic, C!int)); + + static struct ValueArg(int i) {} + static assert( isInstantiationOf!(ValueArg, ValueArg!42)); + static assert( isInstantiationOf!(ValueArg, ValueArg!256)); + static assert( isInstantiationOf!(ValueArg, const ValueArg!1024)); + static assert( isInstantiationOf!(ValueArg, shared ValueArg!1024)); + static assert(!isInstantiationOf!(ValueArg, int)); + static assert(!isInstantiationOf!(ValueArg, S!int)); + + int i; + + static struct AliasArg(alias Symbol) {} + static assert( isInstantiationOf!(AliasArg, AliasArg!42)); + static assert( isInstantiationOf!(AliasArg, AliasArg!int)); + static assert( isInstantiationOf!(AliasArg, AliasArg!i)); + static assert( isInstantiationOf!(AliasArg, const AliasArg!i)); + static assert( isInstantiationOf!(AliasArg, shared AliasArg!i)); + static assert(!isInstantiationOf!(AliasArg, int)); + static assert(!isInstantiationOf!(AliasArg, S!int)); + + // An uninstantiated template is not an instance of any template, + // not even itself. + static assert(!isInstantiationOf!(S, S)); + static assert(!isInstantiationOf!(S, C)); + static assert(!isInstantiationOf!(C, C)); + static assert(!isInstantiationOf!(C, S)); + + // Variables of a templated type are not considered instantiations of that + // type. For templated types, the overload which takes a type must be used. + S!int s; + C!string c; + static assert(!isInstantiationOf!(S, s)); + static assert(!isInstantiationOf!(C, c)); +} + +// Examples of templated functions. +@safe unittest +{ + static int foo(T...)() { return 42; } + static void bar(T...)(T var) {} + static void baz(T)(T var) {} + static bool frobozz(alias pred)(int) { return true; } + + static assert( isInstantiationOf!(foo, foo!int)); + static assert( isInstantiationOf!(foo, foo!string)); + static assert( isInstantiationOf!(foo, foo!(int, string))); + static assert(!isInstantiationOf!(foo, bar!int)); + static assert(!isInstantiationOf!(foo, bar!string)); + static assert(!isInstantiationOf!(foo, bar!(int, string))); + + static assert( isInstantiationOf!(bar, bar!int)); + static assert( isInstantiationOf!(bar, bar!string)); + static assert( isInstantiationOf!(bar, bar!(int, string))); + static assert(!isInstantiationOf!(bar, foo!int)); + static assert(!isInstantiationOf!(bar, foo!string)); + static assert(!isInstantiationOf!(bar, foo!(int, string))); + + static assert( isInstantiationOf!(baz, baz!int)); + static assert( isInstantiationOf!(baz, baz!string)); + static assert(!isInstantiationOf!(baz, foo!(int, string))); + + static assert( isInstantiationOf!(frobozz, frobozz!(a => a))); + static assert( isInstantiationOf!(frobozz, frobozz!(a => a > 2))); + static assert(!isInstantiationOf!(frobozz, baz!int)); + + // Unfortunately, the function type is not considered an instantiation of + // the template, because that information is not part of the type, unlike + // with templated structs or classes. + static assert(!isInstantiationOf!(foo, typeof(foo!int))); + static assert(!isInstantiationOf!(bar, typeof(bar!int))); +} + +// Examples of templates which aren't types or functions. +@safe unittest +{ + template SingleArg(T) {} + template Variadic(T...) {} + template ValueArg(string s) {} + template Alias(alias symbol) {} + + static assert( isInstantiationOf!(SingleArg, SingleArg!int)); + static assert( isInstantiationOf!(SingleArg, SingleArg!string)); + static assert(!isInstantiationOf!(SingleArg, int)); + static assert(!isInstantiationOf!(SingleArg, Variadic!int)); + + static assert( isInstantiationOf!(Variadic, Variadic!())); + static assert( isInstantiationOf!(Variadic, Variadic!int)); + static assert( isInstantiationOf!(Variadic, Variadic!string)); + static assert( isInstantiationOf!(Variadic, Variadic!(short, int, long))); + static assert(!isInstantiationOf!(Variadic, int)); + static assert(!isInstantiationOf!(Variadic, SingleArg!int)); + + static assert( isInstantiationOf!(ValueArg, ValueArg!"dlang")); + static assert( isInstantiationOf!(ValueArg, ValueArg!"foobar")); + static assert(!isInstantiationOf!(ValueArg, string)); + static assert(!isInstantiationOf!(ValueArg, Variadic!string)); + + int i; + + static assert( isInstantiationOf!(Alias, Alias!int)); + static assert( isInstantiationOf!(Alias, Alias!42)); + static assert( isInstantiationOf!(Alias, Alias!i)); + static assert(!isInstantiationOf!(Alias, int)); + static assert(!isInstantiationOf!(Alias, SingleArg!int)); +} + +/// Examples of partial instantation. +@safe unittest +{ + static struct SingleArg(T) {} + static struct Variadic(T...) {} + + alias isSingleArg = isInstantiationOf!SingleArg; + alias isVariadic = isInstantiationOf!Variadic; + + static assert( isSingleArg!(SingleArg!int)); + static assert( isSingleArg!(const SingleArg!int)); + static assert(!isSingleArg!int); + static assert(!isSingleArg!(Variadic!int)); + + static assert( isVariadic!(Variadic!())); + static assert( isVariadic!(Variadic!int)); + static assert( isVariadic!(shared Variadic!int)); + static assert( isVariadic!(Variadic!(int, string))); + static assert(!isVariadic!int); + static assert(!isVariadic!(SingleArg!int)); + + T foo(T)(T t) { return t; } + T likeFoo(T)(T t) { return t; } + bool bar(alias pred)(int i) { return pred(i); } + + alias isFoo = isInstantiationOf!foo; + alias isBar = isInstantiationOf!bar; + + static assert( isFoo!(foo!int)); + static assert( isFoo!(foo!string)); + static assert(!isFoo!int); + static assert(!isFoo!(likeFoo!int)); + static assert(!isFoo!(bar!(a => true))); + + static assert( isBar!(bar!(a => true))); + static assert( isBar!(bar!(a => a > 2))); + static assert(!isBar!int); + static assert(!isBar!(foo!int)); + static assert(!isBar!(likeFoo!int)); +} + /++ Evaluates to an $(D AliasSeq) containing the members of an enum type. From ffe309b06563f8ace3f952a144b038653966e29e Mon Sep 17 00:00:00 2001 From: Inkrementator <70717315+Inkrementator@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:16:18 +0200 Subject: [PATCH 45/57] Remove resolved bug from exmaples https://issues.dlang.org/show_bug.cgi?id=24064 is resolved --- std/range/package.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/range/package.d b/std/range/package.d index d9d74fb21a9..8e8287a5c8d 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -1683,7 +1683,7 @@ pure @safe unittest assert(range.array == [S(5), S(6)]); } -/// https://issues.dlang.org/show_bug.cgi?id=24064 +// https://issues.dlang.org/show_bug.cgi?id=24064 pure @safe nothrow unittest { import std.algorithm.comparison : equal; From 7b05ce70cc305385a5b99cbbe67d544f8227d2ac Mon Sep 17 00:00:00 2001 From: Inkrementator <70717315+Inkrementator@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:18:41 +0200 Subject: [PATCH 46/57] Correct Link formatting --- std/range/package.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/range/package.d b/std/range/package.d index 8e8287a5c8d..c9314796171 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -1716,7 +1716,7 @@ pure @safe nothrow @nogc unittest } } -/// https://issues.dlang.org/show_bug.cgi?id=24243 +/// $(LINK2 https://issues.dlang.org/show_bug.cgi?id=24243, Bug 24243) pure @safe nothrow unittest { import std.algorithm.iteration : filter; From 72832ab8a496a5985f333e8979b6376cc9bbd99f Mon Sep 17 00:00:00 2001 From: Inkrementator <70717315+Inkrementator@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:34:19 +0200 Subject: [PATCH 47/57] Remove test with bugreport from the online documentation --- std/range/package.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/range/package.d b/std/range/package.d index c9314796171..15c8b0879b1 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -1716,7 +1716,7 @@ pure @safe nothrow @nogc unittest } } -/// $(LINK2 https://issues.dlang.org/show_bug.cgi?id=24243, Bug 24243) +// https://issues.dlang.org/show_bug.cgi?id=24243 pure @safe nothrow unittest { import std.algorithm.iteration : filter; From a92d08bcfc64d116fb070ff5fc161605d5022ffd Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 19 Apr 2024 23:54:27 +0200 Subject: [PATCH 48/57] std.bigint: remove subSimple --- std/internal/math/biguintcore.d | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index d5c4768f064..9df6bb22274 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -2241,31 +2241,6 @@ do return carry; } -// result = left - right -// returns carry (0 or 1) -BigDigit subSimple(BigDigit [] result,const(BigDigit) [] left, - const(BigDigit) [] right) pure nothrow -in -{ - assert(result.length == left.length, - "result and left must be of the same length"); - assert(left.length >= right.length, - "left must be longer or of equal length to right"); - assert(right.length > 0, "right must not be empty"); -} -do -{ - BigDigit carry = multibyteSub(result[0 .. right.length], - left[0 .. right.length], right, 0); - if (right.length < left.length) - { - result[right.length .. left.length] = left[right.length .. $]; - carry = multibyteIncrementAssign!('-')(result[right.length..$], carry); - } //else if (result.length == left.length+1) { result[$-1] = carry; carry=0; } - return carry; -} - - /* result = result - right * Returns carry = 1 if result was less than right. */ From 375f738b0c797c5730b89c40403febaf0d61dff8 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 21 Apr 2024 16:57:19 -0600 Subject: [PATCH 49/57] Add FieldNames, FieldSymbols, and FieldTypes to phobos.sys.traits. (#8986) FieldNames is roughly equivalent to std.traits' FieldNameTuple, and FieldTypes is roughly equivalent to std.traits' Fields (which used to be FieldTypeTuple). The primary difference implementation-wise is that the phobos.sys.traits versions require that the types that they're given be aggregate types. For some reason, the std.traits versions accept any type and then try to give a result that at least sort of make sense when they're given a type which isn't an aggregate type (even though they really can't, because a type which isn't an aggregate type has no fields, making any choice kind of arbitrary). For types which aren't aggregate types, Fields gives the type back in an AliasSeq, and FieldNameTuple gives AliasSeq!"". Neither makes any sense to me. I assume that it was done so that those traits could be used in generic code and work with any type, but realistically, if you want to do anything sane with them, you need to already have verified that you're dealing with an aggregate type, since it's just going to be error-prone to do stuff like Fields!int and then get AliasSeq!int back as if it had a single field of type int (or FieldNameTuple!int and get an empty string as the name). So, the phobos.sys.traits versions simply require that you give them aggregate types to avoid that entire mess. FieldNames evaluates to the names of the fields as strings. The "Tuple" in the name of the std.traits version is an artifact from when AliasSeqs were called TypeTuples, so I didn't keep that. FieldTypes evaluates to the types for the fields. It's FieldTypes rather than Fields for clarity, since it's not at all obvious what Fields is supposed to give you (if I'd had to guess, I would have guessed the symbols, not the types). FieldSymbols is new. Its usefulness is questionable, since it does almost exactly the same thing that tupleof does. However, I've included it because of the subtle issues that you get with nested structs - namely that tupleof includes a context pointer in its result, which you probably don't want (though that obviously depends on what you're doing), and FieldNames and FieldTypes don't include it (just like their std.traits counterparts don't), so it seemed like it would make it less error-prone to have FieldSymbols for the cases where symbols are needed. The documentation explains (and the examples show) the difference between FieldSymbols and tupleof so that the programmer can decide which makes more sense for their particular use case. --- phobos/sys/traits.d | 797 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 797 insertions(+) diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 65d56af141b..595dc7a15b6 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -74,6 +74,11 @@ $(LREF isSameSymbol) $(LREF isSameType) )) + $(TR $(TD Aggregate Type Traits) $(TD + $(LREF FieldNames) + $(LREF FieldSymbols) + $(LREF FieldTypes) + )) $(TR $(TD General Types) $(TD $(LREF KeyType) $(LREF OriginalType) @@ -2468,6 +2473,798 @@ template isSameType(T) static assert(indexOf!(isSameType!int, Types) == 2); } +/++ + Evaluates to an $(D AliasSeq) of the names (as $(D string)s) of the member + variables of an aggregate type (i.e. a struct, class, interface, or union). + + These are fields which take up memory space within an instance of the type + (i.e. not enums / manifest constants, since they don't take up memory + space, and not static member variables, since they don't take up memory + space within an instance). + + Hidden fields (like the virtual function table pointer or the context + pointer for nested types) are not included. + + For classes, only the direct member variables are included and not those + of any base classes. + + For interfaces, the result of FieldNames is always empty, because + interfaces cannot have member variables. However, because interfaces are + aggregate types, they work with FieldNames for consistency so that code + that's written to work on aggregate types doesn't have to worry about + whether it's dealing with an interface. + + See_Also: + $(LREF FieldSymbols) + $(LREF FieldTypes) + $(DDSUBLINK spec/struct.html, struct_instance_properties, $(D tupleof)) + +/ +template FieldNames(T) +if (isAggregateType!T) +{ + import phobos.sys.meta : AliasSeq; + + static if (is(T == struct) && __traits(isNested, T)) + private alias Fields = AliasSeq!(T.tupleof[0 .. $ - 1]); + else + private alias Fields = T.tupleof; + + alias FieldNames = AliasSeq!(); + static foreach (Field; Fields) + FieldNames = AliasSeq!(FieldNames, Field.stringof); +} + +/// +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + struct S + { + int x; + float y; + } + static assert(FieldNames!S == AliasSeq!("x", "y")); + + // Since the AliasSeq contains values, all of which are of the same type, + // it can be used to create a dynamic array, which would be more + // efficient than operating on an AliasSeq in the cases where an + // AliasSeq is not necessary. + static assert([FieldNames!S] == ["x", "y"]); + + class C + { + // static variables are not included. + static int var; + + // Manifest constants are not included. + enum lang = "dlang"; + + // Functions are not included, even if they're @property functions. + @property int foo() { return 42; } + + string s; + int i; + int[] arr; + } + static assert(FieldNames!C == AliasSeq!("s", "i", "arr")); + + static assert([FieldNames!C] == ["s", "i", "arr"]); + + // Only direct member variables are included. Member variables from any base + // classes are not. + class D : C + { + real r; + } + static assert(FieldNames!D == AliasSeq!"r"); + + static assert([FieldNames!D] == ["r"]); + + // FieldNames will always be empty for an interface, since it's not legal + // for interfaces to have member variables. + interface I + { + } + static assert(FieldNames!I.length == 0); + + union U + { + int i; + double d; + long l; + S s; + } + static assert(FieldNames!U == AliasSeq!("i", "d", "l", "s")); + + static assert([FieldNames!U] == ["i", "d", "l", "s"]);; + + // FieldNames only operates on aggregate types. + static assert(!__traits(compiles, FieldNames!int)); + static assert(!__traits(compiles, FieldNames!(S*))); + static assert(!__traits(compiles, FieldNames!(C[]))); +} + +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + { + static struct S0 {} + static assert(FieldNames!S0.length == 0); + + static struct S1 { int a; } + static assert(FieldNames!S1 == AliasSeq!"a"); + + static struct S2 { int a; string b; } + static assert(FieldNames!S2 == AliasSeq!("a", "b")); + + static struct S3 { int a; string b; real c; } + static assert(FieldNames!S3 == AliasSeq!("a", "b", "c")); + } + { + int i; + struct S0 { void foo() { i = 0; }} + static assert(FieldNames!S0.length == 0); + static assert(__traits(isNested, S0)); + + struct S1 { int a; void foo() { i = 0; } } + static assert(FieldNames!S1 == AliasSeq!"a"); + static assert(__traits(isNested, S1)); + + struct S2 { int a; string b; void foo() { i = 0; } } + static assert(FieldNames!S2 == AliasSeq!("a", "b")); + static assert(__traits(isNested, S2)); + + struct S3 { int a; string b; real c; void foo() { i = 0; } } + static assert(FieldNames!S3 == AliasSeq!("a", "b", "c")); + static assert(__traits(isNested, S3)); + } + { + static class C0 {} + static assert(FieldNames!C0.length == 0); + + static class C1 { int a; } + static assert(FieldNames!C1 == AliasSeq!"a"); + + static class C2 { int a; string b; } + static assert(FieldNames!C2 == AliasSeq!("a", "b")); + + static class C3 { int a; string b; real c; } + static assert(FieldNames!C3 == AliasSeq!("a", "b", "c")); + + static class D0 : C3 {} + static assert(FieldNames!D0.length == 0); + + static class D1 : C3 { bool x; } + static assert(FieldNames!D1 == AliasSeq!"x"); + + static class D2 : C3 { bool x; int* y; } + static assert(FieldNames!D2 == AliasSeq!("x", "y")); + + static class D3 : C3 { bool x; int* y; short[] z; } + static assert(FieldNames!D3 == AliasSeq!("x", "y", "z")); + } + { + int i; + class C0 { void foo() { i = 0; }} + static assert(FieldNames!C0.length == 0); + static assert(__traits(isNested, C0)); + + class C1 { int a; void foo() { i = 0; } } + static assert(FieldNames!C1 == AliasSeq!"a"); + static assert(__traits(isNested, C1)); + + class C2 { int a; string b; void foo() { i = 0; } } + static assert(FieldNames!C2 == AliasSeq!("a", "b")); + static assert(__traits(isNested, C2)); + + class C3 { int a; string b; real c; void foo() { i = 0; } } + static assert(FieldNames!C3 == AliasSeq!("a", "b", "c")); + static assert(__traits(isNested, C3)); + + class D0 : C3 {} + static assert(FieldNames!D0.length == 0); + static assert(__traits(isNested, D0)); + + class D1 : C3 { bool x; } + static assert(FieldNames!D1 == AliasSeq!"x"); + static assert(__traits(isNested, D1)); + + class D2 : C3 { bool x; int* y; } + static assert(FieldNames!D2 == AliasSeq!("x", "y")); + static assert(__traits(isNested, D2)); + + class D3 : C3 { bool x; int* y; short[] z; } + static assert(FieldNames!D3 == AliasSeq!("x", "y", "z")); + static assert(__traits(isNested, D3)); + } + { + static union U0 {} + static assert(FieldNames!U0.length == 0); + + static union U1 { int a; } + static assert(FieldNames!U1 == AliasSeq!"a"); + + static union U2 { int a; string b; } + static assert(FieldNames!U2 == AliasSeq!("a", "b")); + + static union U3 { int a; string b; real c; } + static assert(FieldNames!U3 == AliasSeq!("a", "b", "c")); + } + { + static struct S + { + enum e = 42; + static str = "foobar"; + + string name() { return "foo"; } + + int[] arr; + + struct Inner1 { int i; } + + static struct Inner2 { long gnol; } + + union { int a; string b; } + + alias Foo = Inner1; + } + + static assert(FieldNames!S == AliasSeq!("arr", "a", "b")); + static assert(FieldNames!(const S) == AliasSeq!("arr", "a", "b")); + static assert(FieldNames!(S.Inner1) == AliasSeq!"i"); + static assert(FieldNames!(S.Inner2) == AliasSeq!"gnol"); + } +} + +/++ + Evaluates to an $(D AliasSeq) of the symbols for the member variables of an + aggregate type (i.e. a struct, class, interface, or union). + + These are fields which take up memory space within an instance of the type + (i.e. not enums / manifest constants, since they don't take up memory + space, and not static member variables, since they don't take up memory + space within an instance). + + Hidden fields (like the virtual function table pointer or the context + pointer for nested types) are not included. + + For classes, only the direct member variables are included and not those + of any base classes. + + For interfaces, the result of FieldSymbols is always empty, because + interfaces cannot have member variables. However, because interfaces are + aggregate types, they work with FieldSymbols for consistency so that code + that's written to work on aggregate types doesn't have to worry about + whether it's dealing with an interface. + + In most cases, $(D FieldSymbols!T) has the same result as $(D T.tupleof). + The difference is that for nested structs with a context pointer, + $(D T.tupleof) includes the context pointer, whereas $(D FieldSymbols!T) + does not. For non-nested structs, and for classes, interfaces, and unions, + $(D FieldSymbols!T) and $(D T.tupleof) are the same. + + So, for most cases, $(D T.tupleof) is sufficient and avoids instantiating + an additional template, but FieldSymbols is provided so that the code that + needs to avoid including context pointers in the list of fields can do so + without the programmer having to figure how to do that correctly. It also + provides a template that's equivalent to what $(LREF FieldNames) and + $(LREF FieldTypes) do in terms of which fields it gives (the difference of + course then being whether you get the symbols, names, or types for the + fields), whereas the behavior for $(D tupleof) is subtly different. + + See_Also: + $(LREF FieldNames) + $(LREF FieldTypes) + $(DDSUBLINK spec/struct.html, struct_instance_properties, $(D tupleof)) + $(DDSUBLINK spec/traits, isNested, $(D __traits(isNested, ...))). + $(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, ...))). + +/ +template FieldSymbols(T) +if (isAggregateType!T) +{ + static if (is(T == struct) && __traits(isNested, T)) + { + import phobos.sys.meta : AliasSeq; + alias FieldSymbols = AliasSeq!(T.tupleof[0 .. $ - 1]); + } + else + alias FieldSymbols = T.tupleof; +} + +/// +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + struct S + { + int x; + float y; + } + static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.x, S.y))); + + // FieldSymbols!S and S.tupleof are the same, because S is not nested. + static assert(__traits(isSame, FieldSymbols!S, S.tupleof)); + + // Note that type qualifiers _should_ be passed on to the result, but due + // to https://issues.dlang.org/show_bug.cgi?id=24516, they aren't. + // FieldTypes does not have this problem, because it aliases the types + // rather than the symbols, so if you need the types from the symbols, you + // should use either FieldTypes or tupleof until the compiler bug has been + // fixed (and if you use tupleof, you need to avoid aliasing the result + // before getting the types from it). + static assert(is(typeof(FieldSymbols!S[0]) == int)); + + // These currently fail when they shouldn't: + //static assert(is(typeof(FieldSymbols!(const S)[0]) == const int)); + //static assert(is(typeof(FieldSymbols!(shared S)[0]) == shared int)); + + class C + { + // static variables are not included. + static int var; + + // Manifest constants are not included. + enum lang = "dlang"; + + // Functions are not included, even if they're @property functions. + @property int foo() { return 42; } + + string s; + int i; + int[] arr; + } + static assert(__traits(isSame, FieldSymbols!C, AliasSeq!(C.s, C.i, C.arr))); + + // FieldSymbols!C and C.tupleof have the same symbols, because they are + // always the same for classes. + static assert(__traits(isSame, FieldSymbols!C, C.tupleof)); + + // Only direct member variables are included. Member variables from any base + // classes are not. + class D : C + { + real r; + } + static assert(__traits(isSame, FieldSymbols!D, AliasSeq!(D.r))); + static assert(__traits(isSame, FieldSymbols!D, D.tupleof)); + + // FieldSymbols will always be empty for an interface, since it's not legal + // for interfaces to have member variables. + interface I + { + } + static assert(FieldSymbols!I.length == 0); + static assert(I.tupleof.length == 0); + + union U + { + int i; + double d; + long l; + S s; + } + static assert(__traits(isSame, FieldSymbols!U, AliasSeq!(U.i, U.d, U.l, U.s))); + + // FieldSymbols!C and C.tupleof have the same symbols, because they are + // always the same for unions. + static assert(__traits(isSame, FieldSymbols!U, U.tupleof)); + + // FieldSymbols only operates on aggregate types. + static assert(!__traits(compiles, FieldSymbols!int)); + static assert(!__traits(compiles, FieldSymbols!(S*))); + static assert(!__traits(compiles, FieldSymbols!(C[]))); +} + +/// Some examples with nested types. +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + int outside; + + struct S + { + long l; + string s; + + void foo() { outside = 2; } + } + static assert(__traits(isNested, S)); + static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.l, S.s))); + + // FieldSymbols!S and S.tupleof are not the same, because S is nested, and + // the context pointer to the outer scope is included in S.tupleof, whereas + // it is excluded from FieldSymbols!S. + static assert(__traits(isSame, S.tupleof[0 .. $ - 1], AliasSeq!(S.l, S.s))); + static assert(S.tupleof[$ - 1].stringof == "this"); + + class C + { + bool b; + int* ptr; + + void foo() { outside = 7; } + } + static assert(__traits(isNested, C)); + static assert(__traits(isSame, FieldSymbols!C, AliasSeq!(C.b, C.ptr))); + + // FieldSymbols!C and C.tupleof have the same symbols, because they are + // always the same for classes. No context pointer is provided as part of + // tupleof for nested classes. + static assert(__traits(isSame, FieldSymbols!C, C.tupleof)); + + // __traits(isNested, ...) is never true for interfaces or unions, since + // they cannot have a context pointer to an outer scope. So, tupleof and + // FieldSymbols will always be the same for interfaces and unions. +} + +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + { + static struct S0 {} + static assert(FieldSymbols!S0.length == 0); + + static struct S1 { int a; } + static assert(__traits(isSame, FieldSymbols!S1, AliasSeq!(S1.a))); + + static struct S2 { int a; string b; } + static assert(__traits(isSame, FieldSymbols!S2, AliasSeq!(S2.a, S2.b))); + + static struct S3 { int a; string b; real c; } + static assert(__traits(isSame, FieldSymbols!S3, AliasSeq!(S3.a, S3.b, S3.c))); + } + { + int i; + struct S0 { void foo() { i = 0; }} + static assert(FieldSymbols!S0.length == 0); + static assert(__traits(isNested, S0)); + + struct S1 { int a; void foo() { i = 0; } } + static assert(__traits(isSame, FieldSymbols!S1, AliasSeq!(S1.a))); + static assert(__traits(isNested, S1)); + + struct S2 { int a; string b; void foo() { i = 0; } } + static assert(__traits(isSame, FieldSymbols!S2, AliasSeq!(S2.a, S2.b))); + static assert(__traits(isNested, S2)); + + struct S3 { int a; string b; real c; void foo() { i = 0; } } + static assert(__traits(isSame, FieldSymbols!S3, AliasSeq!(S3.a, S3.b, S3.c))); + static assert(__traits(isNested, S3)); + } + { + static class C0 {} + static assert(FieldSymbols!C0.length == 0); + + static class C1 { int a; } + static assert(__traits(isSame, FieldSymbols!C1, AliasSeq!(C1.a))); + + static class C2 { int a; string b; } + static assert(__traits(isSame, FieldSymbols!C2, AliasSeq!(C2.a, C2.b))); + + static class C3 { int a; string b; real c; } + static assert(__traits(isSame, FieldSymbols!C3, AliasSeq!(C3.a, C3.b, C3.c))); + + static class D0 : C3 {} + static assert(FieldSymbols!D0.length == 0); + + static class D1 : C3 { bool x; } + static assert(__traits(isSame, FieldSymbols!D1, AliasSeq!(D1.x))); + + static class D2 : C3 { bool x; int* y; } + static assert(__traits(isSame, FieldSymbols!D2, AliasSeq!(D2.x, D2.y))); + + static class D3 : C3 { bool x; int* y; short[] z; } + static assert(__traits(isSame, FieldSymbols!D3, AliasSeq!(D3.x, D3.y, D3.z))); + } + { + int i; + class C0 { void foo() { i = 0; }} + static assert(FieldSymbols!C0.length == 0); + static assert(__traits(isNested, C0)); + + class C1 { int a; void foo() { i = 0; } } + static assert(__traits(isSame, FieldSymbols!C1, AliasSeq!(C1.a))); + static assert(__traits(isNested, C1)); + + class C2 { int a; string b; void foo() { i = 0; } } + static assert(__traits(isSame, FieldSymbols!C2, AliasSeq!(C2.a, C2.b))); + static assert(__traits(isNested, C2)); + + class C3 { int a; string b; real c; void foo() { i = 0; } } + static assert(__traits(isSame, FieldSymbols!C3, AliasSeq!(C3.a, C3.b, C3.c))); + static assert(__traits(isNested, C3)); + + class D0 : C3 {} + static assert(FieldSymbols!D0.length == 0); + static assert(__traits(isNested, D0)); + + class D1 : C3 { bool x; } + static assert(__traits(isSame, FieldSymbols!D1, AliasSeq!(D1.x))); + static assert(__traits(isNested, D1)); + + class D2 : C3 { bool x; int* y; } + static assert(__traits(isSame, FieldSymbols!D2, AliasSeq!(D2.x, D2.y))); + static assert(__traits(isNested, D2)); + + class D3 : C3 { bool x; int* y; short[] z; } + static assert(__traits(isSame, FieldSymbols!D3, AliasSeq!(D3.x, D3.y, D3.z))); + static assert(__traits(isNested, D3)); + } + { + static union U0 {} + static assert(FieldSymbols!U0.length == 0); + + static union U1 { int a; } + static assert(__traits(isSame, FieldSymbols!U1, AliasSeq!(U1.a))); + + static union U2 { int a; string b; } + static assert(__traits(isSame, FieldSymbols!U2, AliasSeq!(U2.a, U2.b))); + + static union U3 { int a; string b; real c; } + static assert(__traits(isSame, FieldSymbols!U3, AliasSeq!(U3.a, U3.b, U3.c))); + } + { + static struct S + { + enum e = 42; + static str = "foobar"; + + string name() { return "foo"; } + + int[] arr; + + struct Inner1 { int i; } + + static struct Inner2 { long gnol; } + + union { int a; string b; } + + alias Foo = Inner1; + } + + static assert(__traits(isSame, FieldSymbols!S, AliasSeq!(S.arr, S.a, S.b))); + static assert(__traits(isSame, FieldSymbols!(const S), AliasSeq!(S.arr, S.a, S.b))); + static assert(__traits(isSame, FieldSymbols!(S.Inner1), AliasSeq!(S.Inner1.i))); + static assert(__traits(isSame, FieldSymbols!(S.Inner2), AliasSeq!(S.Inner2.gnol))); + } +} + +/++ + Evaluates to an $(D AliasSeq) of the types of the member variables of an + aggregate type (i.e. a struct, class, interface, or union). + + These are fields which take up memory space within an instance of the type + (i.e. not enums / manifest constants, since they don't take up memory + space, and not static member variables, since they don't take up memory + space within an instance). + + Hidden fields (like the virtual function table pointer or the context + pointer for nested types) are not included. + + For classes, only the direct member variables are included and not those + of any base classes. + + For interfaces, the result of FieldTypes is always empty, because + interfaces cannot have member variables. However, because interfaces are + aggregate types, they work with FieldTypes for consistency so that code + that's written to work on aggregate types doesn't have to worry about + whether it's dealing with an interface. + + See_Also: + $(LREF FieldNames) + $(LREF FieldSymbols) + $(DDSUBLINK spec/struct.html, struct_instance_properties, $(D tupleof)) + +/ +template FieldTypes(T) +if (isAggregateType!T) +{ + static if (is(T == struct) && __traits(isNested, T)) + alias FieldTypes = typeof(T.tupleof[0 .. $ - 1]); + else + alias FieldTypes = typeof(T.tupleof); +} + +/// +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + struct S + { + int x; + float y; + } + static assert(is(FieldTypes!S == AliasSeq!(int, float))); + + // Type qualifers will be passed on to the result. + static assert(is(FieldTypes!(const S) == AliasSeq!(const int, const float))); + static assert(is(FieldTypes!(shared S) == AliasSeq!(shared int, shared float))); + + class C + { + // static variables are not included. + static int var; + + // Manifest constants are not included. + enum lang = "dlang"; + + // Functions are not included, even if they're @property functions. + @property int foo() { return 42; } + + string s; + int i; + int[] arr; + } + static assert(is(FieldTypes!C == AliasSeq!(string, int, int[]))); + + // Only direct member variables are included. Member variables from any base + // classes are not. + class D : C + { + real r; + } + static assert(is(FieldTypes!D == AliasSeq!real)); + + // FieldTypes will always be empty for an interface, since it's not legal + // for interfaces to have member variables. + interface I + { + } + static assert(FieldTypes!I.length == 0); + + union U + { + int i; + double d; + long l; + S s; + } + static assert(is(FieldTypes!U == AliasSeq!(int, double, long, S))); + + // FieldTypes only operates on aggregate types. + static assert(!__traits(compiles, FieldTypes!int)); + static assert(!__traits(compiles, FieldTypes!(S*))); + static assert(!__traits(compiles, FieldTypes!(C[]))); +} + +@safe unittest +{ + import phobos.sys.meta : AliasSeq; + + { + static struct S0 {} + static assert(FieldTypes!S0.length == 0); + + static struct S1 { int a; } + static assert(is(FieldTypes!S1 == AliasSeq!int)); + + static struct S2 { int a; string b; } + static assert(is(FieldTypes!S2 == AliasSeq!(int, string))); + + static struct S3 { int a; string b; real c; } + static assert(is(FieldTypes!S3 == AliasSeq!(int, string, real))); + } + { + int i; + struct S0 { void foo() { i = 0; }} + static assert(FieldTypes!S0.length == 0); + static assert(__traits(isNested, S0)); + + struct S1 { int a; void foo() { i = 0; } } + static assert(is(FieldTypes!S1 == AliasSeq!int)); + static assert(__traits(isNested, S1)); + + struct S2 { int a; string b; void foo() { i = 0; } } + static assert(is(FieldTypes!S2 == AliasSeq!(int, string))); + static assert(__traits(isNested, S2)); + + struct S3 { int a; string b; real c; void foo() { i = 0; } } + static assert(is(FieldTypes!S3 == AliasSeq!(int, string, real))); + static assert(__traits(isNested, S3)); + } + { + static class C0 {} + static assert(FieldTypes!C0.length == 0); + + static class C1 { int a; } + static assert(is(FieldTypes!C1 == AliasSeq!int)); + + static class C2 { int a; string b; } + static assert(is(FieldTypes!C2 == AliasSeq!(int, string))); + + static class C3 { int a; string b; real c; } + static assert(is(FieldTypes!C3 == AliasSeq!(int, string, real))); + + static class D0 : C3 {} + static assert(FieldTypes!D0.length == 0); + + static class D1 : C3 { bool x; } + static assert(is(FieldTypes!D1 == AliasSeq!bool)); + + static class D2 : C3 { bool x; int* y; } + static assert(is(FieldTypes!D2 == AliasSeq!(bool, int*))); + + static class D3 : C3 { bool x; int* y; short[] z; } + static assert(is(FieldTypes!D3 == AliasSeq!(bool, int*, short[]))); + } + { + int i; + class C0 { void foo() { i = 0; }} + static assert(FieldTypes!C0.length == 0); + static assert(__traits(isNested, C0)); + + class C1 { int a; void foo() { i = 0; } } + static assert(is(FieldTypes!C1 == AliasSeq!int)); + static assert(__traits(isNested, C1)); + + class C2 { int a; string b; void foo() { i = 0; } } + static assert(is(FieldTypes!C2 == AliasSeq!(int, string))); + static assert(__traits(isNested, C2)); + + class C3 { int a; string b; real c; void foo() { i = 0; } } + static assert(is(FieldTypes!C3 == AliasSeq!(int, string, real))); + static assert(__traits(isNested, C3)); + + class D0 : C3 {} + static assert(FieldTypes!D0.length == 0); + static assert(__traits(isNested, D0)); + + class D1 : C3 { bool x; } + static assert(is(FieldTypes!D1 == AliasSeq!bool)); + static assert(__traits(isNested, D1)); + + class D2 : C3 { bool x; int* y; } + static assert(is(FieldTypes!D2 == AliasSeq!(bool, int*))); + static assert(__traits(isNested, D2)); + + class D3 : C3 { bool x; int* y; short[] z; } + static assert(is(FieldTypes!D3 == AliasSeq!(bool, int*, short[]))); + static assert(__traits(isNested, D3)); + } + { + static union U0 {} + static assert(FieldTypes!U0.length == 0); + + static union U1 { int a; } + static assert(is(FieldTypes!U1 == AliasSeq!int)); + + static union U2 { int a; string b; } + static assert(is(FieldTypes!U2 == AliasSeq!(int, string))); + + static union U3 { int a; string b; real c; } + static assert(is(FieldTypes!U3 == AliasSeq!(int, string, real))); + } + { + static struct S + { + enum e = 42; + static str = "foobar"; + + string name() { return "foo"; } + + int[] arr; + + struct Inner1 { int i; } + + static struct Inner2 { long gnol; } + + union { int a; string b; } + + alias Foo = Inner1; + } + + static assert(is(FieldTypes!S == AliasSeq!(int[], int, string))); + static assert(is(FieldTypes!(const S) == AliasSeq!(const(int[]), const int, const string))); + static assert(is(FieldTypes!(S.Inner1) == AliasSeq!int)); + static assert(is(FieldTypes!(S.Inner2) == AliasSeq!long)); + } +} + /++ Takes a type which is an associative array and evaluates to the type of the keys in that associative array. From 4b2ea30979d3cc48f39ea5b7119004ace6df96ac Mon Sep 17 00:00:00 2001 From: 0-v-0 Date: Mon, 22 Apr 2024 21:04:56 +0800 Subject: [PATCH 50/57] Remove std.conv import from createStorageAndFields --- std/bitmanip.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/std/bitmanip.d b/std/bitmanip.d index 717fa2dbb7b..de2ff318f4e 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -164,8 +164,7 @@ private template createStorageAndFields(Ts...) alias StoreType = ulong; else { - import std.conv : to; - static assert(false, "Field widths must sum to 8, 16, 32, or 64, not " ~ to!string(Size)); + static assert(false, "Field widths must sum to 8, 16, 32, or 64, not " ~ Size.stringof); alias StoreType = ulong; // just to avoid another error msg } From 5ce534459330c466ec1da3619ac2665d52b7d99a Mon Sep 17 00:00:00 2001 From: chloekek Date: Fri, 26 Apr 2024 00:56:55 +0200 Subject: [PATCH 51/57] Promote `std.process.Config.preExecFunction` to a delegate std.process.Config.preExecFunction is now a delegate instead of a function pointer, and can therefore capture an environment, for example: import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; import std.process : Config, execute; void runProgram(int pdeathsig) { execute( ["program"], config: Config( preExecFunction: () @trusted => prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, ), ); } Despite function pointers implicitly converting to delegates, this is a backwards-incompatible change, as user code may rely on the field being a function pointer. For example, code like the following will no longer compile: import std.process : Config; nothrow pure @nogc @safe bool f() { return true; } void example() { auto config = Config(preExecFunction: &f); bool function() g = config.preExecFunction; } --- ...process.Config.preExecFunction-delegate.dd | 38 +++++++++++++++++++ std/process.d | 20 +++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 changelog/std.process.Config.preExecFunction-delegate.dd diff --git a/changelog/std.process.Config.preExecFunction-delegate.dd b/changelog/std.process.Config.preExecFunction-delegate.dd new file mode 100644 index 00000000000..e9b13b3c268 --- /dev/null +++ b/changelog/std.process.Config.preExecFunction-delegate.dd @@ -0,0 +1,38 @@ +Promote `std.process.Config.preExecFunction` to a delegate + +$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecFunction, +`std.process.Config.preExecFunction`) is now a delegate instead of a function +pointer, and can therefore capture an environment, for example: + +------- +import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; +import std.process : Config, execute; + +void runProgram(int pdeathsig) +{ + execute( + ["program"], + config: Config( + preExecFunction: () @trusted => + prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, + ), + ); +} +------- + +Despite function pointers implicitly converting to delegates, this is a +backwards-incompatible change, as user code may rely on the field being a +function pointer. For example, code like the following will no longer compile: + +------- +import std.process : Config; + +nothrow pure @nogc @safe +bool f() { return true; } + +void example() +{ + auto config = Config(preExecFunction: &f); + bool function() g = config.preExecFunction; +} +------- diff --git a/std/process.d b/std/process.d index 494910f3535..fbdb2e48ea9 100644 --- a/std/process.d +++ b/std/process.d @@ -1271,6 +1271,22 @@ version (Posix) assert(received); } +version (Posix) +@safe unittest +{ + foreach (i; 0 .. 3) + { + auto config = Config( + preExecFunction: delegate() @trusted { + _Exit(i); + return true; + }, + ); + auto pid = spawnProcess(["false"], config: config); + assert(wait(pid) == i); + } +} + /* Implementation of spawnProcess() for Windows. @@ -2188,11 +2204,11 @@ struct Config On Windows, this member is not available. */ - bool function() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecFunction; } else version (Posix) { - bool function() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecFunction; } } From 7a280a938e48d96cca5e01bdd5c5ae71bd502114 Mon Sep 17 00:00:00 2001 From: chloekek Date: Fri, 26 Apr 2024 00:56:55 +0200 Subject: [PATCH 52/57] Add std.process.Config.preExecDelegate std.process.Config.preExecDelegate is just like std.process.Config.preExecFunction, but can capture an environment, for example: import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; import std.process : Config, execute; void runProgram(int pdeathsig) { execute( ["program"], config: Config( preExecDelegate: () @trusted => prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, ), ); } preExecFunction is retained for backwards compatibility. If both preExecFunction and preExecDelegate are given, both are called. --- .../std.process.Config.preExecDelegate.dd | 25 ++++++++++ ...process.Config.preExecFunction-delegate.dd | 38 --------------- std/process.d | 46 ++++++++++++++++--- 3 files changed, 64 insertions(+), 45 deletions(-) create mode 100644 changelog/std.process.Config.preExecDelegate.dd delete mode 100644 changelog/std.process.Config.preExecFunction-delegate.dd diff --git a/changelog/std.process.Config.preExecDelegate.dd b/changelog/std.process.Config.preExecDelegate.dd new file mode 100644 index 00000000000..393d26cf0b9 --- /dev/null +++ b/changelog/std.process.Config.preExecDelegate.dd @@ -0,0 +1,25 @@ +Add `std.process.Config.preExecDelegate` + +$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecDelegate, `std.process.Config.preExecDelegate`) +is just like +$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecFunction, `std.process.Config.preExecFunction`), +but can capture an environment, for example: + +------- +import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; +import std.process : Config, execute; + +void runProgram(int pdeathsig) +{ + execute( + ["program"], + config: Config( + preExecDelegate: () @trusted => + prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, + ), + ); +} +------- + +`preExecFunction` is retained for backwards compatibility. If both +`preExecFunction` and `preExecDelegate` are given, both are called. diff --git a/changelog/std.process.Config.preExecFunction-delegate.dd b/changelog/std.process.Config.preExecFunction-delegate.dd deleted file mode 100644 index e9b13b3c268..00000000000 --- a/changelog/std.process.Config.preExecFunction-delegate.dd +++ /dev/null @@ -1,38 +0,0 @@ -Promote `std.process.Config.preExecFunction` to a delegate - -$(LINK2 $(ROOT_DIR)phobos/std_process.html#.Config.preExecFunction, -`std.process.Config.preExecFunction`) is now a delegate instead of a function -pointer, and can therefore capture an environment, for example: - -------- -import core.sys.linux.sys.prctl : PR_SET_PDEATHSIG, prctl; -import std.process : Config, execute; - -void runProgram(int pdeathsig) -{ - execute( - ["program"], - config: Config( - preExecFunction: () @trusted => - prctl(PR_SET_PDEATHSIG, pdeathsig, 0, 0, 0) != -1, - ), - ); -} -------- - -Despite function pointers implicitly converting to delegates, this is a -backwards-incompatible change, as user code may rely on the field being a -function pointer. For example, code like the following will no longer compile: - -------- -import std.process : Config; - -nothrow pure @nogc @safe -bool f() { return true; } - -void example() -{ - auto config = Config(preExecFunction: &f); - bool function() g = config.preExecFunction; -} -------- diff --git a/std/process.d b/std/process.d index fbdb2e48ea9..325689ba51d 100644 --- a/std/process.d +++ b/std/process.d @@ -1102,6 +1102,14 @@ private Pid spawnProcessPosix(scope const(char[])[] args, } } + if (config.preExecDelegate !is null) + { + if (config.preExecDelegate() != true) + { + abortOnError(forkPipeOut, InternalError.preExec, .errno); + } + } + // Execute program. core.sys.posix.unistd.execve(argz[0], argz.ptr, envz); @@ -1187,7 +1195,7 @@ private Pid spawnProcessPosix(scope const(char[])[] args, errorMsg = "Failed to allocate memory"; break; case InternalError.preExec: - errorMsg = "Failed to execute preExecFunction"; + errorMsg = "Failed to execute preExecFunction or preExecDelegate"; break; case InternalError.noerror: assert(false); @@ -1272,18 +1280,25 @@ version (Posix) } version (Posix) -@safe unittest +@system unittest { + __gshared int j; foreach (i; 0 .. 3) { auto config = Config( - preExecFunction: delegate() @trusted { - _Exit(i); + preExecFunction: function() @trusted { + j = 1; + return true; + }, + preExecDelegate: delegate() @trusted { + // j should now be 1, as preExecFunction is called before + // preExecDelegate is. + _Exit(i + j); return true; }, ); auto pid = spawnProcess(["false"], config: config); - assert(wait(pid) == i); + assert(wait(pid) == i + 1); } } @@ -2202,13 +2217,30 @@ struct Config Please note that the code in this function must only use async-signal-safe functions.) + If $(LREF preExecDelegate) is also set, it is called last. + + On Windows, this member is not available. + */ + bool function() nothrow @nogc @safe preExecFunction; + + /** + A delegate that is called before `exec` in $(LREF spawnProcess). + It returns `true` if succeeded and otherwise returns `false`. + + $(RED Warning: + Please note that the code in this function must only use + async-signal-safe functions.) + + If $(LREF preExecFunction) is also set, it is called first. + On Windows, this member is not available. */ - bool delegate() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecDelegate; } else version (Posix) { - bool delegate() nothrow @nogc @safe preExecFunction; + bool function() nothrow @nogc @safe preExecFunction; + bool delegate() nothrow @nogc @safe preExecDelegate; } } From 140484a4bbfefcc2624f3bd70eb4c5010f11ca59 Mon Sep 17 00:00:00 2001 From: Adam Wilson Date: Sun, 28 Apr 2024 20:32:06 -0700 Subject: [PATCH 53/57] Add DUB install to VM initialization. (#8992) --- .github/workflows/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ed27c241e60..df7dbe21bd3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,6 +51,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 50 + + - name: Install Host D compiler + uses: dlang-community/setup-dlang@v1 + - name: Clone DMD run: | set -uexo pipefail @@ -131,6 +135,10 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 50 + + - name: Install Host D compiler + uses: dlang-community/setup-dlang@v1 + - name: Run in VM uses: cross-platform-actions/action@v0.23.0 with: From 9a8325ca5f3269ce1154baf1da8ea2b491e834aa Mon Sep 17 00:00:00 2001 From: dokutoku <3581664+dokutoku@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:08:10 +0900 Subject: [PATCH 54/57] Fix some invalid links (#8994) Co-authored-by: dokutoku <3729541-dokutoku@users.noreply.gitlab.com> --- std/algorithm/setops.d | 2 +- std/socket.d | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index cc6f5b77db7..363bd16a0f9 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -8,7 +8,7 @@ $(LREF setIntersection), $(LREF setSymmetricDifference) expect a range of sorted ranges as input. All algorithms are generalized to accept as input not only sets but also -$(HTTP https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm +$(LINK2 https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm documents behaviour in the presence of duplicated inputs. $(SCRIPT inhibitQuickIndex = 1;) diff --git a/std/socket.d b/std/socket.d index b2570f596ca..e86a51fe2df 100644 --- a/std/socket.d +++ b/std/socket.d @@ -1925,7 +1925,7 @@ version (StdDdoc) * auto abstractAddr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR"); * --- * - * See_Also: $(HTTP http://man7.org/linux/man-pages/man7/unix.7.html, UNIX(7)) + * See_Also: $(HTTP man7.org/linux/man-pages/man7/unix.7.html, UNIX(7)) */ class UnixAddress: Address { From 449b8e12faa2e03b1db3dffcadd27afb40ad578c Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sat, 4 May 2024 12:55:46 +0200 Subject: [PATCH 55/57] Revert "Add DUB install to VM initialization. (#8992)" (#8999) This reverts commit 140484a4bbfefcc2624f3bd70eb4c5010f11ca59. --- .github/workflows/main.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index df7dbe21bd3..ed27c241e60 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,10 +51,6 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 50 - - - name: Install Host D compiler - uses: dlang-community/setup-dlang@v1 - - name: Clone DMD run: | set -uexo pipefail @@ -135,10 +131,6 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 50 - - - name: Install Host D compiler - uses: dlang-community/setup-dlang@v1 - - name: Run in VM uses: cross-platform-actions/action@v0.23.0 with: From f9fa1354fa242be694de689740e79c82141f09dc Mon Sep 17 00:00:00 2001 From: Adam Wilson Date: Tue, 7 May 2024 16:30:23 -0700 Subject: [PATCH 56/57] Remove OMF flags from Makefile (#8993) --- Makefile | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Makefile b/Makefile index f8fe1c62f7b..89704a431ee 100644 --- a/Makefile +++ b/Makefile @@ -128,10 +128,6 @@ ifeq (,$(findstring win,$(OS))) ifneq (osx,$(OS)) NODEFAULTLIB += -L-lpthread -L-lm endif -else - ifeq ($(MODEL),32omf) - CPPFLAGS := -DNO_snprintf - endif endif # Set CC, CC_OUTFILEFLAG and CFLAGS unless using importC @@ -142,9 +138,6 @@ ifneq ($(USE_IMPORTC),1) ifeq ($(OS),win32wine) CC := wine dmc.exe CFLAGS := $(if $(findstring $(BUILD),debug),-g,-O) - else ifeq ($(MODEL),32omf) - CC := dmc - CFLAGS := $(if $(findstring $(BUILD),debug),-g,-O) else ifeq ($(OS),windows) CC := cl CC_OUTFILEFLAG := /Fo @@ -202,9 +195,7 @@ ifeq (,$(findstring win,$(OS))) SONAME:=libphobos2.so.$(MAJOR).$(MINOR) LIBSO:=$(ROOT)/$(SONAME).$(PATCH) else - ifeq ($(MODEL),32omf) - LIB:=phobos.lib - else ifeq ($(MODEL),32) + ifeq ($(MODEL),32) LIB:=phobos32mscoff.lib else LIB:=phobos$(MODEL).lib From 1a8d2eae06f0d37dd137517f51115dfc79276b0b Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 7 May 2024 23:12:50 +0800 Subject: [PATCH 57/57] std/math/hardware.d: Fix compile error for LoongArch ``` std/math/hardware.d(187):1:19: error: invalid operand for instruction movfcsr2gr $a0, $r2 ``` --- std/math/hardware.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/math/hardware.d b/std/math/hardware.d index 4bfe792428c..501245ba90b 100644 --- a/std/math/hardware.d +++ b/std/math/hardware.d @@ -165,7 +165,7 @@ private: uint result = void; asm pure nothrow @nogc { - "movfcsr2gr %0,$r2" : "=r" (result); + "movfcsr2gr %0, $fcsr2" : "=r" (result); } return result & EXCEPTIONS_MASK; } @@ -883,7 +883,7 @@ private: ControlState cont; asm pure nothrow @nogc { - "movfcsr2gr %0,$r0" : "=r" (cont); + "movfcsr2gr %0, $fcsr0" : "=r" (cont); } cont &= (roundingMask | allExceptions); return cont;