Re: enforce (i > 0) for i = int.min does not throw

2018-01-31 Thread Steven Schveighoffer via Digitalmars-d-learn

On 1/31/18 6:19 PM, Azi Hassan wrote:

On Saturday, 27 January 2018 at 14:13:49 UTC, kdevel wrote:

I would expect this code

enforce3.d
---
import std.exception;

void main ()
{
   int i = int.min;
   enforce (i > 0);
}
---

to throw an "Enforcement failed" exception, but it doesn't:

$ dmd enforce3.d
$ ./enforce3
[nothing]


I wonder if it's caused by a comparison between signed and unsigned 
integers.


No, the answer is, there's a shortcut optimization used by the compiler. 
See the discussion elsewhere in this thread.




import std.stdio;

void main ()
{
     int zero = 0;
     writeln(int.min > 0u);
     writeln(int.min > zero);
}


Note that comparing the literal int.min will get folded into a constant, 
and do the right thing. You have to assign it a variable to see the 
incorrect behavior:


int i = int.min;
writeln(i > 0);

-Steve


Re: enforce (i > 0) for i = int.min does not throw

2018-01-31 Thread Azi Hassan via Digitalmars-d-learn

On Saturday, 27 January 2018 at 14:13:49 UTC, kdevel wrote:

I would expect this code

enforce3.d
---
import std.exception;

void main ()
{
   int i = int.min;
   enforce (i > 0);
}
---

to throw an "Enforcement failed" exception, but it doesn't:

$ dmd enforce3.d
$ ./enforce3
[nothing]


I wonder if it's caused by a comparison between signed and 
unsigned integers.


import std.stdio;

void main ()
{
int zero = 0;
writeln(int.min > 0u);
writeln(int.min > zero);
}

$ rdmd test.d
true
false

The same behavior can be observed in C :

#include 
#include 

int main(void)
{
int zero = 0;
printf("%d\n", INT_MIN > 0u);
printf("%d\n", INT_MIN > zero);
return 0;
}

$ gcc test.c && ./a.out
1
0


Re: enforce (i > 0) for i = int.min does not throw

2018-01-31 Thread Steven Schveighoffer via Digitalmars-d-learn

On 1/30/18 3:37 PM, kdevel wrote:

On Sunday, 28 January 2018 at 19:17:49 UTC, Steven Schveighoffer wrote:
This is insane. i > 0 is used in so many places. The only saving grace 
appears to be that int.min is just so uncommonly seen in the wild.


And another one that it does not happen when compiled with optimization 
(-O) and also that it does not affect all the ints:


---
import std.stdio;

void foo (T) ()
{
    auto i = T.min;
    writefln ("%12s: %24X %12s", T.stringof, i, i > cast(T) 0);
}

void main ()
{
    foo!byte;
    foo!short;
    foo!int;
    foo!long;
}
---

     byte:   80    false
    short: 8000    false
  int: 8000 true
     long: 8000 true



This is due to integer promotion 
(https://dlang.org/spec/type.html#usual-arithmetic-conversions). Any 
operation between two non-integers, first the two operands are promoted 
to integers.


You can see the result here:

https://run.dlang.io/is/RAk9tE


In 32 bit mode:

     byte:   80    false
    short: 8000    false
  int: 8000 true
     long: 8000    false



Most likely, this is due to the fact that working with longs cannot be 
done natively by the CPU, so it can't use the same shifting shortcut 
that causes the issue in the first place.


-Steve


Re: enforce (i > 0) for i = int.min does not throw

2018-01-30 Thread kdevel via Digitalmars-d-learn
On Sunday, 28 January 2018 at 19:17:49 UTC, Steven Schveighoffer 
wrote:
This is insane. i > 0 is used in so many places. The only 
saving grace appears to be that int.min is just so uncommonly 
seen in the wild.


And another one that it does not happen when compiled with 
optimization (-O) and also that it does not affect all the ints:


---
import std.stdio;

void foo (T) ()
{
   auto i = T.min;
   writefln ("%12s: %24X %12s", T.stringof, i, i > cast(T) 0);
}

void main ()
{
   foo!byte;
   foo!short;
   foo!int;
   foo!long;
}
---

byte:   80false
   short: 8000false
 int: 8000 true
long: 8000 true

In 32 bit mode:

byte:   80false
   short: 8000false
 int: 8000 true
long: 8000false



Re: enforce (i > 0) for i = int.min does not throw

2018-01-28 Thread Seb via Digitalmars-d-learn
On Sunday, 28 January 2018 at 19:17:49 UTC, Steven Schveighoffer 
wrote:

On 1/27/18 9:50 AM, ag0aep6g wrote:


Wow, that looks really bad.

Apparently, dmd implements `i < 0` as a `i >> 31`. I.e., it 
shifts the bits to the right so far that only the sign bit is 
left. This is ok.


But it implements `i > 0` as `(-i) >> 31`. That would be 
correct if negation would always flip the sign bit. But it 
doesn't for `int.min`. `-int.min` is `int.min` again.


So dmd emits wrong code for `i > 0`. O_O

I've filed an issue:
https://issues.dlang.org/show_bug.cgi?id=18315


This is insane. i > 0 is used in so many places. The only 
saving grace appears to be that int.min is just so uncommonly 
seen in the wild.


I tested all the way back to 2.040, still has the same behavior.

-Steve



FYI/OT: If you need to check the behavior of old compilers, the 
"All" option at run.dlang.io might be helpful.


It starts from 2.060 and it works best for consistent output 
(i.e. without stacktrace pointer).

Examples:

- https://run.dlang.io/is/IoN3sj (code from the bug report)
- https://run.dlang.io/is/LuxUQ5 (code from the bug report, 
slightly modified)
- https://run.dlang.io/is/3R4r1U (simple example of "when was the 
symbol added to Phobos?")


(It's based on Vladimir's regression tester and can be used 
locally too: https://github.com/dlang-tour/core-dreg)


Re: enforce (i > 0) for i = int.min does not throw

2018-01-28 Thread Steven Schveighoffer via Digitalmars-d-learn

On 1/27/18 9:50 AM, ag0aep6g wrote:


Wow, that looks really bad.

Apparently, dmd implements `i < 0` as a `i >> 31`. I.e., it shifts the 
bits to the right so far that only the sign bit is left. This is ok.


But it implements `i > 0` as `(-i) >> 31`. That would be correct if 
negation would always flip the sign bit. But it doesn't for `int.min`. 
`-int.min` is `int.min` again.


So dmd emits wrong code for `i > 0`. O_O

I've filed an issue:
https://issues.dlang.org/show_bug.cgi?id=18315


This is insane. i > 0 is used in so many places. The only saving grace 
appears to be that int.min is just so uncommonly seen in the wild.


I tested all the way back to 2.040, still has the same behavior.

-Steve


Re: enforce (i > 0) for i = int.min does not throw

2018-01-27 Thread kdevel via Digitalmars-d-learn

On Saturday, 27 January 2018 at 14:49:52 UTC, Ali Çehreli wrote:
But enforce is a red herring there. This prints true with 2.078 
as well:


import std.stdio;

void main ()
{
int i = int.min;
writeln(i > 0);// prints 'true' with 2.078
}


test.d
---
import std.stdio;
void main ()
{
   int i = int.min;
   auto b = i > 0;
   b.writeln;
   auto c = int.min > 0;
   c.writeln;
}
---
$ dmd test.d
$ ./test
true
false



Re: enforce (i > 0) for i = int.min does not throw

2018-01-27 Thread ag0aep6g via Digitalmars-d-learn

On 01/27/2018 03:13 PM, kdevel wrote:

I would expect this code

enforce3.d
---
import std.exception;

void main ()
{
    int i = int.min;
    enforce (i > 0);
}
---

to throw an "Enforcement failed" exception, but it doesn't:

$ dmd enforce3.d
$ ./enforce3
[nothing]




Wow, that looks really bad.

Apparently, dmd implements `i < 0` as a `i >> 31`. I.e., it shifts the 
bits to the right so far that only the sign bit is left. This is ok.


But it implements `i > 0` as `(-i) >> 31`. That would be correct if 
negation would always flip the sign bit. But it doesn't for `int.min`. 
`-int.min` is `int.min` again.


So dmd emits wrong code for `i > 0`. O_O

I've filed an issue:
https://issues.dlang.org/show_bug.cgi?id=18315


Re: enforce (i > 0) for i = int.min does not throw

2018-01-27 Thread Ali Çehreli via Digitalmars-d-learn

On 01/27/2018 06:13 AM, kdevel wrote:

I would expect this code

enforce3.d
---
import std.exception;

void main ()
{
    int i = int.min;
    enforce (i > 0);
}
---

to throw an "Enforcement failed" exception, but it doesn't:

$ dmd enforce3.d
$ ./enforce3
[nothing]




Looks like a major issue to me.

But enforce is a red herring there. This prints true with 2.078 as well:

import std.stdio;

void main ()
{
int i = int.min;
writeln(i > 0);// prints 'true' with 2.078
}

Ali


enforce (i > 0) for i = int.min does not throw

2018-01-27 Thread kdevel via Digitalmars-d-learn

I would expect this code

enforce3.d
---
import std.exception;

void main ()
{
   int i = int.min;
   enforce (i > 0);
}
---

to throw an "Enforcement failed" exception, but it doesn't:

$ dmd enforce3.d
$ ./enforce3
[nothing]