February 15, 2010
Andrei Alexandrescu wrote:
> Justin Johansson wrote:
>> Andrei Alexandrescu wrote:
>>> ulong x0;
>>> static assert(!__traits(compiles, -x0));
>>> uint x1;
>>> static assert(!__traits(compiles, -x1));
>>> ushort x2;
>>> static assert(!__traits(compiles, -x2));
>>> ubyte x3;
>>> static assert(!__traits(compiles, -x3));
>>>
>>> Sounds good?
>>>
>>> Andrei
>>
>> Sounds excellent.  Who would have thought of that?
>>
>> Cheers
>> Justin Johansson
>>
> 
> Actually Walter just talked me into forgetting about it. -x is conceptually rewritten into ~x + 1 for all types and typed accordingly. I'm dropping this in order to keep focused on more important changes.
> 
> Andrei

I just want to add I'm glad to see this is staying since I use (x & -x) rather frequently on unsigned types to get one set bit. Obviously I could just expand it to the full ~x + 1 but the current idiom is nice, short and easily recognizable.

Janzert
February 15, 2010
On Mon, 15 Feb 2010 17:21:09 -0500, Walter Bright
<newshound1@digitalmars.com> wrote:

> Steven Schveighoffer wrote:
>> are there any good cases besides this that Walter has?  And even if there are, we are not talking about silently mis-interpreting it.  There is precedent for making valid C code an error because it is error prone.
>
>
> Here's where I'm coming from with this. The problem is that CPU integers are 2's complement and a fixed number of bits. We'd like to pretend they work just like whole numbers we learned about in 2nd grade arithmetic. But they don't, and we can't fix it so they do. I think it's ultimately fruitless to try and make them behave other than what they are: 2's complement fixed arrays of bits.
>
> So, we wind up with oddities like overflow, wrap-around, -int.min==int.min. Heck, we *rely* on these oddities (subtraction depends on wrap-around). Sometimes, we pretend these bit values are signed, sometimes unsigned, and we mix together those notions in the same expression.

The counter-point to your point is that a programming language is not fed
to the CPU, it is fed to a compiler.  The compiler must make the most it
can of what it sees in source code, but it can help the user express
himself to the CPU.  The problem is, when you have ambiguous statements,
the compiler can either choose an interpretation or throw an error.
There's nothing wrong with throwing an error if the statement is ambiguous
or nonsensical.

The alternative (which is what we have today) that the actual meaning is
most of the time not what the user wants.

A more graphic example is something like this:

string x = 1;

What did the user mean?  Did he mean, make a string out of 1 and assign it
to x, or did he mistype the type of x?  Throwing an error is perfectly
acceptable here, I don't see why the same isn't true for:

uint x = -1;

> There's no way to not mix up signed and unsigned arithmetic.
>
> Trying to build walls between signed and unsigned integer types is an exercise in utter futility. They are both 2-s complement bits, and it's best to treat them that way rather than pretend they aren't.
>
> As for -x in particular, - is not negation. It's complement and increment, and produces exactly the same bit result for signed and unsigned types. If it is disallowed for unsigned integers, then the user is faced with either:
>
>     (~x + 1)
>
> which not only looks weird in an arithmetic expression, but then a special case for it has to be wired into the optimizer to turn it back into a NEG instruction.

I should clarify, using - on an unsigned value should work, it just should
not be assignable to an unsigned type.  I guess I disagree with the
original statement for this post (that it should be disabled all
together), but I think that the compiler should avoid something that is
99% of the time an error.

i.e.

uint a = -1; // error
uint b = 5;
uint c = -b; // error
int d = -b; // ok
auto e = -b; // e is type int

In the case of literals, I think allowing - on a literal should require
that it be assigned to a signed type or involve a cast.

-Steve
February 16, 2010
On 2010-02-15 18:33:11 -0500, "Steven Schveighoffer" <schveiguy@yahoo.com> said:

> I should clarify, using - on an unsigned value should work, it just should
> not be assignable to an unsigned type.  I guess I disagree with the
> original statement for this post (that it should be disabled all
> together), but I think that the compiler should avoid something that is
> 99% of the time an error.
> 
> i.e.
> 
> uint a = -1; // error
> uint b = 5;
> uint c = -b; // error
> int d = -b; // ok
> auto e = -b; // e is type int

But should this work?

uint a = 0-1;
uint c = 0-b;
auto e = 0-b; // e is type int?

uint zero = 0;
uint a = zero-1;
uint c = zero-b;
auto e = zero-b; // e is type int?

This rule has good intentions, but it brings some strange inconsistencies. The current rules are much easier to predict since they behave always the same whether you have a variable, a literal or a constant expression.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

February 16, 2010
On 2010-02-15 17:21:09 -0500, Walter Bright <newshound1@digitalmars.com> said:

> Or:
> 
>     -cast(int)x
> 
> That blows when x happens to be a ulong. Whoops. It blows even worse if x turns out to be a struct with overloaded opNeg and opCast, suddenly the opCast gets selected. Oops.

That one is easy to fix. Add a "signed(x)" template function returning the signed counterpart of x. You could even insert a runtime range check if you really wanted to.


-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

February 16, 2010
Michel Fortin wrote:
> That one is easy to fix. Add a "signed(x)" template function returning the signed counterpart of x. You could even insert a runtime range check if you really wanted to.

Yes, I mentioned that at the end of that post, and the reasons why it is  not very appealing.
February 16, 2010
Some of the ways C uses fixnums, its undefined situations, are bad today. The Ada language is not handy to use, but it shows that if you want to create reliable software able to fly planes, you want a language with a more tidy arithmetic than C.

And I am not talking about multiprecision numbers here, there are many situations where fixnums are enough (even if I think in any D program there are some or many places where using a fixnum is a premature optimization).

In a tidy language if you have an integral value represented with a fixed number of bits (like a D uint), and you try to assign it a value (like a -1) outside the range of the values it can represent, you have a bug. Or you want modulo arithmetic, but you have to help the compiler tell such two situations apart.

You can't think just about what DMD2 is/does today: once integral overflow tests are added to a future D2 compiler, don't you want a runtime error if you assign to a number a value outside the range of the possible values it can represent (like a negative value to an unsigned value)?

I am not sure.

Bye,
bearophile
February 16, 2010
On 02/15/2010 05:33 PM, Steven Schveighoffer wrote:
>
> i.e.
>
> uint a = -1; // error

I can't say I would appreciate having to write

uint a = 0xFFFFFFFF;

or the equivalent for ulong.

> uint b = 5;
> uint c = -b; // error
> int d = -b; // ok
> auto e = -b; // e is type int
>
> In the case of literals, I think allowing - on a literal should require
> that it be assigned to a signed type or involve a cast.
>
> -Steve

February 16, 2010
Ellery Newcomer wrote:
> On 02/15/2010 05:33 PM, Steven Schveighoffer wrote:
>> uint a = -1; // error
> 
> I can't say I would appreciate having to write
> 
> uint a = 0xFFFFFFFF;
> 
> or the equivalent for ulong.

uint a = ~0u;


-- 
Rainer Deyke - rainerd@eldwood.com
February 16, 2010
== Quote from Ellery Newcomer (ellery-newcomer@utulsa.edu)'s article
> On 02/15/2010 05:33 PM, Steven Schveighoffer wrote:
> >
> > i.e.
> >
> > uint a = -1; // error
> I can't say I would appreciate having to write
> uint a = 0xFFFFFFFF;
> or the equivalent for ulong.

I always just use uint.max for such things.  Yes, it's a little more typing, but it's clearer and saves me a few seconds of thinking.
February 16, 2010
bearophile wrote:
> Some of the ways C uses fixnums, its undefined situations, are bad
> today. The Ada language is not handy to use, but it shows that if you
> want to create reliable software able to fly planes, you want a
> language with a more tidy arithmetic than C.

1. Ada seems to be a language people use only when they're forced to. D doesn't have the US government forcing its use.

2. Neither Ada nor its design decisions seem to have caught on with other languages, despite being around for 30 years.

3. Plenty of military and civil avionics use C++ anyway, despite attempts by the US government to force Ada.


This is an old, old *old* issue. All the various solutions tried over the decades have failed to catch on. This leads me to believe that either these obvious solutions do not work, or they cause more problems than they fix.

Heaven knows we are trying out a lot of new design ideas in D, but I'm not eager to embrace ideas that have failed over and over for 30 years.