Skip to content
This repository was archived by the owner on Jun 20, 2019. It is now read-only.

Commit 471ac45

Browse files
committed
Fix hashing of complex reals
by backporting dlang/druntime#2356 From druntime commit 29ce0543cb62229f005b2bc8540416dbccd1130e
1 parent b15af9e commit 471ac45

File tree

4 files changed

+815
-417
lines changed

4 files changed

+815
-417
lines changed

libphobos/libdruntime/core/internal/convert.d

Lines changed: 94 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* This module provides functions to converting different values to const(ubyte)[]
44
*
55
* Copyright: Copyright Igor Stepanov 2013-2013.
6-
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6+
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
77
* Authors: Igor Stepanov
88
* Source: $(DRUNTIMESRC core/internal/_convert.d)
99
*/
@@ -33,7 +33,7 @@ private ubyte[] ctfe_alloc()(size_t n)
3333
}
3434
}
3535

36-
@trusted pure nothrow
36+
@trusted pure nothrow @nogc
3737
const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) ||
3838
is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
3939
{
@@ -72,7 +72,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqua
7272
ulong mantissa2 = parsed.mantissa2;
7373
off_bytes--; // go back one, since mantissa only stored data in 56
7474
// bits, ie 7 bytes
75-
for(; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes)
75+
for (; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes)
7676
{
7777
buff[off_bytes] = cast(ubyte)mantissa2;
7878
mantissa2 >>= 8;
@@ -114,13 +114,13 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqua
114114
}
115115
}
116116

117-
@safe pure nothrow
117+
@safe pure nothrow @nogc
118118
private Float parse(bool is_denormalized = false, T)(T x) if (is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
119119
{
120120
return parse(x.im);
121121
}
122122

123-
@safe pure nothrow
123+
@safe pure nothrow @nogc
124124
private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!T != FloatFormat.Real80)
125125
{
126126
Unqual!T x = x_;
@@ -178,7 +178,7 @@ private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!
178178
}
179179
}
180180

181-
@safe pure nothrow
181+
@safe pure nothrow @nogc
182182
private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80)
183183
{
184184
Unqual!T x = x_;
@@ -232,6 +232,7 @@ private struct Float
232232

233233
private template FloatTraits(T) if (floatFormat!T == FloatFormat.Float)
234234
{
235+
enum DATASIZE = 4;
235236
enum EXPONENT = 8;
236237
enum MANTISSA = 23;
237238
enum ZERO = Float(0, 0, 0);
@@ -244,6 +245,7 @@ private template FloatTraits(T) if (floatFormat!T == FloatFormat.Float)
244245

245246
private template FloatTraits(T) if (floatFormat!T == FloatFormat.Double)
246247
{
248+
enum DATASIZE = 8;
247249
enum EXPONENT = 11;
248250
enum MANTISSA = 52;
249251
enum ZERO = Float(0, 0, 0);
@@ -256,6 +258,7 @@ private template FloatTraits(T) if (floatFormat!T == FloatFormat.Double)
256258

257259
private template FloatTraits(T) if (floatFormat!T == FloatFormat.Real80)
258260
{
261+
enum DATASIZE = 10;
259262
enum EXPONENT = 15;
260263
enum MANTISSA = 64;
261264
enum ZERO = Float(0, 0, 0);
@@ -268,6 +271,7 @@ private template FloatTraits(T) if (floatFormat!T == FloatFormat.Real80)
268271

269272
private template FloatTraits(T) if (floatFormat!T == FloatFormat.DoubleDouble) //Unsupported in CTFE
270273
{
274+
enum DATASIZE = 16;
271275
enum EXPONENT = 11;
272276
enum MANTISSA = 106;
273277
enum ZERO = Float(0, 0, 0);
@@ -280,6 +284,7 @@ private template FloatTraits(T) if (floatFormat!T == FloatFormat.DoubleDouble) /
280284

281285
private template FloatTraits(T) if (floatFormat!T == FloatFormat.Quadruple)
282286
{
287+
enum DATASIZE = 16;
283288
enum EXPONENT = 15;
284289
enum MANTISSA = 112;
285290
enum ZERO = Float(0, 0, 0);
@@ -291,10 +296,10 @@ private template FloatTraits(T) if (floatFormat!T == FloatFormat.Quadruple)
291296
}
292297

293298

294-
@safe pure nothrow
299+
@safe pure nothrow @nogc
295300
private real binPow2(int pow)
296301
{
297-
static real binPosPow2(int pow) @safe pure nothrow
302+
static real binPosPow2(int pow) @safe pure nothrow @nogc
298303
{
299304
assert(pow > 0);
300305

@@ -319,14 +324,14 @@ private real binPow2(int pow)
319324

320325

321326
//Need in CTFE, because CTFE float and double expressions computed more precisely that run-time expressions.
322-
@safe pure nothrow
327+
@safe pure nothrow @nogc
323328
private ulong shiftrRound(ulong x)
324329
{
325330
return (x >> 1) + (x & 1);
326331
}
327332

328-
@safe pure nothrow
329-
private uint binLog2(T)(T x)
333+
@safe pure nothrow @nogc
334+
private uint binLog2(T)(const T x)
330335
{
331336
assert(x > 0);
332337
int max = 2 ^^ (FloatTraits!T.EXPONENT-1)-1;
@@ -353,7 +358,7 @@ private uint binLog2(T)(T x)
353358
return max;
354359
}
355360

356-
@safe pure nothrow
361+
@safe pure nothrow @nogc
357362
private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Real80)
358363
{
359364
x *= 2.0L^^FloatTraits!T.MANTISSA;
@@ -362,7 +367,7 @@ private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == Float
362367
return Float(fl.mantissa >> pow, 0, sign);
363368
}
364369

365-
@safe pure nothrow
370+
@safe pure nothrow @nogc
366371
private Float denormalizedMantissa(T)(T x, uint sign)
367372
if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double)
368373
{
@@ -372,7 +377,7 @@ private Float denormalizedMantissa(T)(T x, uint sign)
372377
return Float(shiftrRound(mant), 0, sign);
373378
}
374379

375-
@safe pure nothrow
380+
@safe pure nothrow @nogc
376381
private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Quadruple)
377382
{
378383
x *= 2.0L^^FloatTraits!T.MANTISSA;
@@ -496,17 +501,17 @@ version (unittest)
496501
testNumberConvert!("real.min_normal/2UL^^63");
497502
// check subnormal storage edge case for Quadruple
498503
testNumberConvert!("real.min_normal/2UL^^56");
499-
//testNumberConvert!("real.min_normal/19"); // XGDC: ct[0] == 0, rt[0] == 27
500-
//testNumberConvert!("real.min_normal/17"); // XGDC: ct[0= == 128, rt[0] == 136
504+
testNumberConvert!("real.min_normal/19");
505+
testNumberConvert!("real.min_normal/17");
501506

502507
/**Test imaginary values: convert algorithm is same with real values*/
503508
testNumberConvert!("0.0Fi");
504509
testNumberConvert!("0.0i");
505510
testNumberConvert!("0.0Li");
506511

507512
/**True random values*/
508-
//testNumberConvert!("-0x9.0f7ee55df77618fp-13829L"); //XGDC: ct[0,1] == [0,96], rt[0,1] == [143,97]
509-
//testNumberConvert!("0x7.36e6e2640120d28p+8797L"); // XGDC: ct[0,1] == [0,24], rt[0,1] == [80,26]
513+
testNumberConvert!("-0x9.0f7ee55df77618fp-13829L");
514+
testNumberConvert!("0x7.36e6e2640120d28p+8797L");
510515
testNumberConvert!("-0x1.05df6ce4702ccf8p+15835L");
511516
testNumberConvert!("0x9.54bb0d88806f714p-7088L");
512517

@@ -567,22 +572,29 @@ template floatFormat(T) if (is(T:real) || is(T:ireal))
567572

568573
}
569574

575+
package template floatSize(T) if (is(T:real) || is(T:ireal))
576+
{
577+
enum floatSize = FloatTraits!(T).DATASIZE;
578+
}
579+
570580
// all toUbyte functions must be evaluable at compile time
571-
@trusted pure nothrow
572-
const(ubyte)[] toUbyte(T)(T[] arr) if (T.sizeof == 1)
581+
@trusted pure nothrow @nogc
582+
const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof == 1)
573583
{
574584
return cast(const(ubyte)[])arr;
575585
}
576586

577-
@trusted pure nothrow
578-
const(ubyte)[] toUbyte(T)(T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyte)[])) && (T.sizeof > 1))
587+
@trusted pure nothrow @nogc
588+
const(ubyte)[] toUbyte(T)(const T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyte)[])) && (T.sizeof > 1))
579589
{
580590
if (__ctfe)
581591
{
582-
const(ubyte)[] ret;
592+
ubyte[] ret = ctfe_alloc(T.sizeof * arr.length);
593+
size_t offset = 0;
583594
foreach (cur; arr)
584595
{
585-
ret ~= toUbyte(cur);
596+
ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof];
597+
offset += T.sizeof;
586598
}
587599
return ret;
588600
}
@@ -592,14 +604,16 @@ const(ubyte)[] toUbyte(T)(T[] arr) if ((is(typeof(toUbyte(arr[0])) == const(ubyt
592604
}
593605
}
594606

595-
@trusted pure nothrow
596-
const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enum))
607+
@trusted pure nothrow @nogc
608+
const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
597609
{
598610
static if (T.sizeof == 1)
599611
{
600612
if (__ctfe)
601613
{
602-
return cast(const(ubyte)[])[val];
614+
ubyte[] result = ctfe_alloc(1);
615+
result[0] = cast(ubyte) val;
616+
return result;
603617
}
604618
else
605619
{
@@ -608,7 +622,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu
608622
}
609623
else if (__ctfe)
610624
{
611-
ubyte[T.sizeof] tmp;
625+
ubyte[] tmp = ctfe_alloc(T.sizeof);
612626
Unqual!T val_ = val;
613627
for (size_t i = 0; i < T.sizeof; ++i)
614628
{
@@ -618,53 +632,91 @@ const(ubyte)[] toUbyte(T)(ref T val) if (__traits(isIntegral, T) && !is(T == enu
618632
tmp[idx] = cast(ubyte)(val_&0xff);
619633
val_ >>= 8;
620634
}
621-
return tmp[].dup;
635+
return tmp;
622636
}
623637
else
624638
{
625639
return (cast(const(ubyte)*)(&val))[0 .. T.sizeof];
626640
}
627641
}
628642

629-
@trusted pure nothrow
630-
const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal))
643+
@trusted pure nothrow @nogc
644+
const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == __vector))
645+
{
646+
if (!__ctfe)
647+
return (cast(const ubyte*) &val)[0 .. T.sizeof];
648+
else static if (is(typeof(val[0]) : void))
649+
assert(0, "Unable to compute byte representation of " ~ T.stringof ~ " at compile time.");
650+
else
651+
{
652+
// This code looks like it should work in CTFE but it segfaults:
653+
// auto a = val.array;
654+
// return toUbyte(a);
655+
alias E = typeof(val[0]);
656+
ubyte[] result = ctfe_alloc(T.sizeof);
657+
for (size_t i = 0, j = 0; i < T.sizeof; i += E.sizeof, ++j)
658+
{
659+
result[i .. i + E.sizeof] = toUbyte(val[j]);
660+
}
661+
return result;
662+
}
663+
}
664+
665+
@trusted pure nothrow @nogc
666+
const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal))
631667
{
632668
if (__ctfe)
633669
{
634670
auto re = val.re;
635671
auto im = val.im;
636-
return (re.toUbyte() ~ im.toUbyte());
672+
auto a = re.toUbyte();
673+
auto b = im.toUbyte();
674+
ubyte[] result = ctfe_alloc(a.length + b.length);
675+
result[0 .. a.length] = a[0 .. a.length];
676+
result[a.length .. $] = b[0 .. b.length];
677+
return result;
637678
}
638679
else
639680
{
640681
return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
641682
}
642683
}
643684

644-
@trusted pure nothrow
645-
const(ubyte)[] toUbyte(T)(ref T val) if (is(T == enum) && is(typeof(toUbyte(cast(V)val)) == const(ubyte)[]))
685+
@trusted pure nothrow @nogc
686+
const(ubyte)[] toUbyte(T)(const ref T val) if (is(T V == enum) && is(typeof(toUbyte(cast(const V)val)) == const(ubyte)[]))
646687
{
647688
if (__ctfe)
648689
{
649690
static if (is(T V == enum)){}
650-
V e_val = val;
651-
return toUbyte(e_val);
691+
return toUbyte(cast(const V) val);
652692
}
653693
else
654694
{
655695
return (cast(const(ubyte)*)&val)[0 .. T.sizeof];
656696
}
657697
}
658698

659-
private bool isNonReference(T)()
699+
nothrow pure @safe unittest
700+
{
701+
// Issue 19008 - check toUbyte works on enums.
702+
enum Month : uint { jan = 1}
703+
Month m = Month.jan;
704+
const bytes = toUbyte(m);
705+
enum ctfe_works = (() => { Month x = Month.jan; return toUbyte(x).length > 0; })();
706+
}
707+
708+
package(core.internal) bool isNonReference(T)()
660709
{
661710
static if (is(T == struct) || is(T == union))
662711
{
663712
return isNonReferenceStruct!T();
664713
}
665714
else static if (__traits(isStaticArray, T))
666715
{
667-
return isNonReference!(typeof(T.init[0]))();
716+
static if (T.length > 0)
717+
return isNonReference!(typeof(T.init[0]))();
718+
else
719+
return true;
668720
}
669721
else static if (is(T E == enum))
670722
{
@@ -698,12 +750,12 @@ private bool isNonReferenceStruct(T)() if (is(T == struct) || is(T == union))
698750
return true;
699751
}
700752

701-
@trusted pure nothrow
702-
const(ubyte)[] toUbyte(T)(ref T val) if (is(T == struct) || is(T == union))
753+
@trusted pure nothrow @nogc
754+
const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == struct) || is(T == union))
703755
{
704756
if (__ctfe)
705757
{
706-
ubyte[T.sizeof] bytes;
758+
ubyte[] bytes = ctfe_alloc(T.sizeof);
707759
foreach (key, cur; val.tupleof)
708760
{
709761
alias CUR_TYPE = typeof(cur);
@@ -722,7 +774,7 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(T == struct) || is(T == union))
722774
assert(0, "Unable to compute byte representation of "~typeof(CUR_TYPE).stringof~" field at compile time");
723775
}
724776
}
725-
return bytes[].dup;
777+
return bytes;
726778
}
727779
else
728780
{

0 commit comments

Comments
 (0)