11/**
2- * This module contains compiler support determining equality of dynamic arrays.
2+ * This module contains compiler support determining equality of arrays.
33 *
4- * Copyright: Copyright Digital Mars 2000 - 2019 .
4+ * Copyright: Copyright Digital Mars 2000 - 2020 .
55 * License: Distributed under the
66 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
77 * (See accompanying file LICENSE)
@@ -23,112 +23,47 @@ bool __ArrayEq(T1, T2)(T1[] a, T2[] b)
2323 return true ;
2424}
2525
26- // `lhs == rhs` lowers to `__equals(lhs, rhs)` for dynamic arrays
27- bool __equals (T1 , T2 )(T1 [] lhs, T2 [] rhs)
26+ // The compiler lowers `lhs == rhs` to `__equals(lhs, rhs)` for
27+ // * dynamic arrays,
28+ // * (most) arrays of different (unqualified) element types, and
29+ // * arrays of structs with custom opEquals.
30+ bool __equals (T1 , T2 )(scope T1 [] lhs, scope T2 [] rhs)
2831{
29- import core.internal.traits : Unqual;
30- alias U1 = Unqual! T1 ;
31- alias U2 = Unqual! T2 ;
32-
33- static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }
34- static @trusted R trustedCast(R, S)(S[] r) { return cast (R) r; }
35-
3632 if (lhs.length != rhs.length)
3733 return false ;
3834
39- if (lhs.length == 0 && rhs.length == 0 )
35+ if (lhs.length == 0 )
4036 return true ;
4137
42- static if (is (U1 == void ) && is (U2 == void ))
43- {
44- return __equals (trustedCast! (ubyte [])(lhs), trustedCast! (ubyte [])(rhs));
45- }
46- else static if (is (U1 == void ))
47- {
48- return __equals (trustedCast! (ubyte [])(lhs), rhs);
49- }
50- else static if (is (U2 == void ))
51- {
52- return __equals (lhs, trustedCast! (ubyte [])(rhs));
53- }
54- else static if (! is (U1 == U2 ))
38+ static if (useMemcmp! (T1 , T2 ))
5539 {
56- // This should replace src/object.d _ArrayEq which
57- // compares arrays of different types such as long & int,
58- // char & wchar.
59- // Compiler lowers to __ArrayEq in dmd/src/opover.d
60- foreach (const u; 0 .. lhs.length)
61- {
62- if (at(lhs, u) != at(rhs, u))
63- return false ;
64- }
65- return true ;
66- }
67- else static if (__traits(isIntegral, U1 ))
68- {
69-
7040 if (! __ctfe)
7141 {
72- import core.stdc.string : memcmp;
73- return () @trusted { return memcmp(cast (void * )lhs.ptr, cast (void * )rhs.ptr, lhs.length * U1 .sizeof) == 0 ; }();
42+ static bool trustedMemcmp (scope T1 [] lhs, scope T2 [] rhs) @trusted @nogc nothrow pure
43+ {
44+ pragma (inline, true );
45+ import core.stdc.string : memcmp;
46+ return memcmp (cast (void * ) lhs.ptr, cast (void * ) rhs.ptr, lhs.length * T1 .sizeof) == 0 ;
47+ }
48+ return trustedMemcmp (lhs, rhs);
7449 }
7550 else
7651 {
77- foreach (const u ; 0 .. lhs.length)
52+ foreach (const i ; 0 .. lhs.length)
7853 {
79- if (at(lhs, u ) != at(rhs, u ))
54+ if (at(lhs, i ) != at(rhs, i ))
8055 return false ;
8156 }
8257 return true ;
8358 }
8459 }
8560 else
8661 {
87- foreach (const u ; 0 .. lhs.length)
62+ foreach (const i ; 0 .. lhs.length)
8863 {
89- static if (__traits(compiles, __equals(at(lhs, u), at(rhs, u))))
90- {
91- if (! __equals(at(lhs, u), at(rhs, u)))
92- return false ;
93- }
94- else static if (__traits(isFloating, U1 ))
95- {
96- if (at(lhs, u) != at(rhs, u))
97- return false ;
98- }
99- else static if (is (U1 : Object ) && is (U2 : Object ))
100- {
101- if (! (cast (Object )at(lhs, u) is cast (Object )at(rhs, u)
102- || at(lhs, u) && (cast (Object )at(lhs, u)).opEquals (cast (Object )at(rhs, u))))
103- return false ;
104- }
105- else static if (__traits(hasMember, U1 , " opEquals" ))
106- {
107- if (! at(lhs, u).opEquals (at(rhs, u)))
108- return false ;
109- }
110- else static if (is (U1 == delegate ))
111- {
112- if (at(lhs, u) != at(rhs, u))
113- return false ;
114- }
115- else static if (is (U1 == U11 * , U11 ))
116- {
117- if (at(lhs, u) != at(rhs, u))
118- return false ;
119- }
120- else static if (__traits(isAssociativeArray, U1 ))
121- {
122- if (at(lhs, u) != at(rhs, u))
123- return false ;
124- }
125- else
126- {
127- if (at(lhs, u).tupleof != at(rhs, u).tupleof)
128- return false ;
129- }
64+ if (at(lhs, i) != at(rhs, i))
65+ return false ;
13066 }
131-
13267 return true ;
13368 }
13469}
@@ -203,3 +138,79 @@ bool __equals(T1, T2)(T1[] lhs, T2[] rhs)
203138 assert (! __equals(a1, a2));
204139 assert (a1 != a2);
205140}
141+
142+
143+ private :
144+
145+ // - Recursively folds static array types to their element type,
146+ // - maps void to ubyte, and
147+ // - pointers to size_t.
148+ template BaseType (T)
149+ {
150+ static if (__traits(isStaticArray, T))
151+ alias BaseType = BaseType! (typeof (T.init[0 ]));
152+ else static if (is (immutable T == immutable void ))
153+ alias BaseType = ubyte ;
154+ else static if (is (T == E* , E))
155+ alias BaseType = size_t ;
156+ else
157+ alias BaseType = T;
158+ }
159+
160+ // Use memcmp if the element sizes match and both base element types are integral.
161+ // Due to int promotion, disallow small integers of diverging signed-ness though.
162+ template useMemcmp (T1 , T2 )
163+ {
164+ static if (T1 .sizeof != T2 .sizeof)
165+ enum useMemcmp = false ;
166+ else
167+ {
168+ alias B1 = BaseType! T1 ;
169+ alias B2 = BaseType! T2 ;
170+ enum useMemcmp = __traits(isIntegral, B1 ) && __traits(isIntegral, B2 )
171+ && ! ( (B1 .sizeof < 4 || B2 .sizeof < 4 ) && __traits(isUnsigned, B1 ) != __traits(isUnsigned, B2 ) );
172+ }
173+ }
174+
175+ unittest
176+ {
177+ enum E { foo, bar }
178+
179+ static assert (useMemcmp! (byte , byte ));
180+ static assert (useMemcmp! (ubyte , ubyte ));
181+ static assert (useMemcmp! (void , const void ));
182+ static assert (useMemcmp! (void , immutable bool ));
183+ static assert (useMemcmp! (void , inout char ));
184+ static assert (useMemcmp! (void , shared ubyte ));
185+ static assert (! useMemcmp! (void , byte )); // differing signed-ness
186+ static assert (! useMemcmp! (char [8 ], byte [8 ])); // ditto
187+
188+ static assert (useMemcmp! (short , short ));
189+ static assert (useMemcmp! (wchar , ushort ));
190+ static assert (! useMemcmp! (wchar , short )); // differing signed-ness
191+
192+ static assert (useMemcmp! (int , uint )); // no promotion, ignoring signed-ness
193+ static assert (useMemcmp! (dchar , E));
194+
195+ static assert (useMemcmp! (immutable void * , size_t ));
196+ static assert (useMemcmp! (double * , ptrdiff_t ));
197+ static assert (useMemcmp! (long [2 ][3 ], const (ulong )[2 ][3 ]));
198+
199+ static assert (! useMemcmp! (float , float ));
200+ static assert (! useMemcmp! (double [2 ], double [2 ]));
201+ static assert (! useMemcmp! (Object , Object ));
202+ static assert (! useMemcmp! (int [], int []));
203+ }
204+
205+ // Returns a reference to an array element, eliding bounds check and
206+ // casting void to ubyte.
207+ pragma (inline, true )
208+ ref at (T)(T[] r, size_t i) @trusted
209+ // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959
210+ if (! (is (T == struct ) && ! is (typeof (T.sizeof))))
211+ {
212+ static if (is (immutable T == immutable void ))
213+ return (cast (ubyte * ) r.ptr)[i];
214+ else
215+ return r.ptr[i];
216+ }
0 commit comments