www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why do shift operators undergo integral promotion?

reply Don <nospam nospam.com> writes:
 From a discussion on D.learn.

If x and y are different integral types, then in an expression like
   x >> y
the integral promotion rules are applied to x and y.
This behaviour is obviously inherited from C, but why did C use such a 
counter-intuitive and bug-prone rule?
Why isn't typeof(x >> y) simply typeof(x) ?
What would break if it did?

You might think the the rule is that typeof( x >> y) is typeof( x + y),
but it isn't: the arithmetic conversions are NOT applied:
typeof(int >> long) is int, not long, BUT
typeof(short >> int) is int.
And we have this death trap (bug 2809):

void main()
{
   short s = -1;
   ushort u = s;
   assert( u == s );
   assert ( (s >>> 1) == (u >>> 1) ); // FAILS
}
Aug 09 2011
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/9/2011 2:46 AM, Don wrote:
  From a discussion on D.learn.

 If x and y are different integral types, then in an expression like
 x >> y
 the integral promotion rules are applied to x and y.
 This behaviour is obviously inherited from C, but why did C use such a
 counter-intuitive and bug-prone rule?
 Why isn't typeof(x >> y) simply typeof(x) ?
 What would break if it did?

 You might think the the rule is that typeof( x >> y) is typeof( x + y),
 but it isn't: the arithmetic conversions are NOT applied:
 typeof(int >> long) is int, not long, BUT
 typeof(short >> int) is int.
 And we have this death trap (bug 2809):

 void main()
 {
 short s = -1;
 ushort u = s;
 assert( u == s );
 assert ( (s >>> 1) == (u >>> 1) ); // FAILS
 }
That last is why we can't just change the behavior from C.
Aug 09 2011
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On 8/9/2011 2:46 AM, Don wrote:
 From a discussion on D.learn.
 
 If x and y are different integral types, then in an expression like
 x >> y
 the integral promotion rules are applied to x and y.
 This behaviour is obviously inherited from C, but why did C use such a
 counter-intuitive and bug-prone rule?
 Why isn't typeof(x >> y) simply typeof(x) ?
 What would break if it did?
 
 You might think the the rule is that typeof( x >> y) is typeof( x + y),
 but it isn't: the arithmetic conversions are NOT applied:
 typeof(int >> long) is int, not long, BUT
 typeof(short >> int) is int.
 And we have this death trap (bug 2809):
 
 void main()
 {
 short s = -1;
 ushort u = s;
 assert( u == s );
 assert ( (s >>> 1) == (u >>> 1) ); // FAILS
 }
That last is why we can't just change the behavior from C.
The question though is whether that is ever _desired_ behavior in a C program. If it's always a bug when it happens, then I'd argue that we can and should change the behavior. If there's a legitimate reason why could would want the C behavior, then changing it in D would cause problems for porting code, but if the difference only matters when there's a bug in the C code, then breaking compatibility is only an issue for broken code, and changing it would help prevent issues in D. - Jonathan M Davis
Aug 09 2011
parent reply so <so so.xn--s-fka> writes:
On Tue, 09 Aug 2011 21:37:18 +0300, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On 8/9/2011 2:46 AM, Don wrote:
 From a discussion on D.learn.

 If x and y are different integral types, then in an expression like
 x >> y
 the integral promotion rules are applied to x and y.
 This behaviour is obviously inherited from C, but why did C use such a
 counter-intuitive and bug-prone rule?
 Why isn't typeof(x >> y) simply typeof(x) ?
 What would break if it did?

 You might think the the rule is that typeof( x >> y) is typeof( x +  
y),
 but it isn't: the arithmetic conversions are NOT applied:
 typeof(int >> long) is int, not long, BUT
 typeof(short >> int) is int.
 And we have this death trap (bug 2809):

 void main()
 {
 short s = -1;
 ushort u = s;
 assert( u == s );
 assert ( (s >>> 1) == (u >>> 1) ); // FAILS
 }
That last is why we can't just change the behavior from C.
The question though is whether that is ever _desired_ behavior in a C program. If it's always a bug when it happens, then I'd argue that we can and should change the behavior. If there's a legitimate reason why could would want the C behavior, then changing it in D would cause problems for porting code, but if the difference only matters when there's a bug in the C code, then breaking compatibility is only an issue for broken code, and changing it would help prevent issues in D. - Jonathan M Davis
Can't we finally get rid of this compatibility issue by separating? . for the whole file which can be enabled with a flag to compiler "dmd -Cmode ...." . default it is not enforced, if we desire compatibility we could just write: version(Cmode) // or something better instead of version { } What would be the problem other than the trouble of implementing this? I hate to see the shield of C compatibility against every good idea/solution.
Aug 09 2011
next sibling parent reply Andrew Wiley <wiley.andrew.j gmail.com> writes:
On Tue, Aug 9, 2011 at 1:10 PM, so <so so.xn--s-fka> wrote:

 On Tue, 09 Aug 2011 21:37:18 +0300, Jonathan M Davis <jmdavisProg gmx.com>
 wrote:

  On 8/9/2011 2:46 AM, Don wrote:
 From a discussion on D.learn.

 If x and y are different integral types, then in an expression like
 x >> y
 the integral promotion rules are applied to x and y.
 This behaviour is obviously inherited from C, but why did C use such a
 counter-intuitive and bug-prone rule?
 Why isn't typeof(x >> y) simply typeof(x) ?
 What would break if it did?

 You might think the the rule is that typeof( x >> y) is typeof( x + y),
 but it isn't: the arithmetic conversions are NOT applied:
 typeof(int >> long) is int, not long, BUT
 typeof(short >> int) is int.
 And we have this death trap (bug 2809):

 void main()
 {
 short s = -1;
 ushort u = s;
 assert( u == s );
 assert ( (s >>> 1) == (u >>> 1) ); // FAILS
 }
That last is why we can't just change the behavior from C.
The question though is whether that is ever _desired_ behavior in a C program. If it's always a bug when it happens, then I'd argue that we can and should change the behavior. If there's a legitimate reason why could would want the C behavior, then changing it in D would cause problems for porting code, but if the difference only matters when there's a bug in the C code, then breaking compatibility is only an issue for broken code, and changing it would help prevent issues in D. - Jonathan M Davis
Can't we finally get rid of this compatibility issue by separating? . for the whole file which can be enabled with a flag to compiler "dmd -Cmode ...."
That would essentially mean we have two compilers to maintain at the end of the day because the semantics of certain operations would depend entirely on whether the compiler was in C mode or not.
Aug 09 2011
parent reply so <so so.xn--s-fka> writes:
On Tue, 09 Aug 2011 23:20:02 +0300, Andrew Wiley  
<wiley.andrew.j gmail.com> wrote:

 Can't we finally get rid of this compatibility issue by separating?
 . for the whole file which can be enabled with a flag to compiler "dmd
 -Cmode ...."
That would essentially mean we have two compilers to maintain at the end of the day because the semantics of certain operations would depend entirely on whether the compiler was in C mode or not.
True, but we are now doing something similar (probably something much harder) which unlikely produces a positive outcome. We say there is a compatibility where there was not (to which extend no one knows), on the other hand there are new rules/syntax for the same problems in the same language which would create awful amount of confusion, not to mention ignoring the solutions to the problems. On maintaining two compilers, i don't think this is a bigger issue because C compiler and spec already here and never changes.
Aug 09 2011
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On Tue, 09 Aug 2011 23:20:02 +0300, Andrew Wiley
 
 <wiley.andrew.j gmail.com> wrote:
 Can't we finally get rid of this compatibility issue by separating?
 . for the whole file which can be enabled with a flag to compiler "dmd
 -Cmode ...."
That would essentially mean we have two compilers to maintain at the end of the day because the semantics of certain operations would depend entirely on whether the compiler was in C mode or not.
True, but we are now doing something similar (probably something much harder) which unlikely produces a positive outcome. We say there is a compatibility where there was not (to which extend no one knows), on the other hand there are new rules/syntax for the same problems in the same language which would create awful amount of confusion, not to mention ignoring the solutions to the problems. On maintaining two compilers, i don't think this is a bigger issue because C compiler and spec already here and never changes.
Generally speaking, when C or C++ code compiles as D code, it has identical behavior (there are a few exceptions - such as passing static arrays - but it's almost always true). The reason for this is so that you don't end up with tons of bugs when porting code to D from C or C++. If it weren't for that, then corner cases such as this wouldn't really matter, and we could just make D to the smart thing. But because we don't want code which is ported to break in all kinds of subtle ways, we maintain compatibility with C/C++ as long as the C/C++ code is valid D code. There's no reason to have a separate version of the language which is more compatible with C than another version. It would just complicate things. Not only would it be a burden on the compiler maintainers, but then you would have to know whether code was intended to be compiled with the standard D compiler or with the one which is more compatible with C. It would be a mess for very little benefit. The question here is whether we can safely make the change to D's behavior without breaking code which is ported from C or C++. As long as it is pretty much a guarantee that any code which relies on the C behavior is buggy, then I don't see any reason why we can't fix the behavior in D. However, if there are valid reasons for C code to rely on the C behavior, then we're kind of stuck.
From what I can see though, it sure looks like any C code which relies on the 
C behavior would be buggy. - Jonathan M Davis
Aug 09 2011
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 There's no reason to have a separate version of the language which is more 
 compatible with C than another version. It would just complicate things. Not 
 only would it be a burden on the compiler maintainers, but then you would have 
 to know whether code was intended to be compiled with the standard D compiler 
 or with the one which is more compatible with C. It would be a mess for very 
 little benefit.
You are too much quick at dismissing this. There are several advantages you aren't even listing. Bye, bearophile
Aug 09 2011
prev sibling next sibling parent reply so <so so.xn--s-fka> writes:
On Wed, 10 Aug 2011 00:08:02 +0300, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 There's no reason to have a separate version of the language which is  
 more
 compatible with C than another version. It would just complicate things.
 Not
 only would it be a burden on the compiler maintainers, but then you  
 would have
 to know whether code was intended to be compiled with the standard D  
 compiler
 or with the one which is more compatible with C. It would be a mess for  
 very
 little benefit.
I see having a compiler flag is a bad idea, it would be entirely in the source code. It is not about having more/less compatible compilers. There is only one compiler to be maintained and it is D. Getting rid of such a restriction (C compatibility) would be priceless i can't express, and i don't understand why it is not obvious. Our problem is this very restriction. D should be called "C like (easy to learn if you know C/C++) and supports C natively", not "C compatible". Because if being out of date was the only thing wrong about C then someone would just add some modern features and call it maybe... C++-+%?
 The question here is whether we can safely make the change to D's  
 behavior
 without breaking code which is ported from C or C++. As long as it is  
 pretty
 much a guarantee that any code which relies on the C behavior is buggy,  
 then I
 don't see any reason why we can't fix the behavior in D. However, if  
 there are
 valid reasons for C code to rely on the C behavior, then we're kind of  
 stuck.
 From what I can see though, it sure looks like any C code which relies  
 on the
C behavior would be buggy.
We are falling a trap C didn't. How is it any different than maintaining ASM compatibility in C? But they didn't, they just separated it. They had a very compelling argument, so much more important than ours, that ASM is THE machine itself. Yet they just dropped it. c code: .... asm { .... } and d would be better of like: ... asm { ... } ... C { }
Aug 09 2011
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On Wed, 10 Aug 2011 00:08:02 +0300, Jonathan M Davis <jmdavisProg gmx.com>
 
 wrote:
 There's no reason to have a separate version of the language which is
 more
 compatible with C than another version. It would just complicate things.
 
 Not
 only would it be a burden on the compiler maintainers, but then you
 would have
 to know whether code was intended to be compiled with the standard D
 compiler
 or with the one which is more compatible with C. It would be a mess for
 very
 little benefit.
I see having a compiler flag is a bad idea, it would be entirely in the source code. It is not about having more/less compatible compilers. There is only one compiler to be maintained and it is D. Getting rid of such a restriction (C compatibility) would be priceless i can't express, and i don't understand why it is not obvious. Our problem is this very restriction. D should be called "C like (easy to learn if you know C/C++) and supports C natively", not "C compatible". Because if being out of date was the only thing wrong about C then someone would just add some modern features and call it maybe... C++-+%?
In general, it's not a problem at all. It's only relevant when the C/C++ code is valid D code. It often isn't. For the most part, the code which is both (the primary exception that I can think of being pointers). It's stuff which is common across C-based languages and would screw over programmers pretty thoroughly if it changed. There are a few exceptions (such as this issue with the shift operator) which are a problem, but for the most part, it's really not an issue. It's trying to maintain C++'s level of compatibility with C where you really get into trouble. D does _not_ maintain anything even close to that level of compatability. It just chooses to be compatible when the syntax is identical so that code which is ported from C or C++ doesn't silently break. So, while from what I can see, I think that in this particular case, it would be a good idea to fix C's behavior rather than follow it, I think that you're blowing the issue of general C compatibility out of proportion. D breaks compatibility all over the place. It just doesn't do it when C code would be valid D code. - Jonathan M Davis
Aug 09 2011
parent reply so <so so.xn--s-fka> writes:
On Wed, 10 Aug 2011 00:57:17 +0300, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 I see having a compiler flag is a bad idea, it would be entirely in the
 source code.
 It is not about having more/less compatible compilers. There is only one
 compiler to be maintained and it is D.
 Getting rid of such a restriction (C compatibility) would be priceless i
 can't express, and i don't understand why it is not obvious.
 Our problem is this very restriction. D should be called "C like (easy  
 to
 learn if you know C/C++) and supports C natively", not "C compatible".
 Because if being out of date was the only thing wrong about C then  
 someone
 would just add some modern features and call it maybe... C++-+%?
In general, it's not a problem at all. It's only relevant when the C/C++ code is valid D code. It often isn't. For the most part, the code which is both as well (the primary exception that I can think of being pointers). It's stuff which is common across C-based languages and would screw over programmers pretty thoroughly if it changed. There are a few exceptions (such as this issue with the shift operator) which are a problem, but for the most part, it's really not an issue. It's trying to maintain C++'s level of compatibility with C where you really get into trouble. D does _not_ maintain anything even close to that level of compatability. It just chooses to be compatible when the syntax is identical so that code which is ported from C or C++ doesn't silently break.
What you are saying just covers the language being a C like language, aren't we talking about the restriction and threat it pose to the language?
 D breaks compatibility all over the place. It just doesn't do it when C  
 code would be valid D code.
I am afraid this is not quite right. Wouldn't it be oxymoron when you reject every solution/idea using this argument? It doesn't matter if the idea has no merit or it was the meaning of life.
Aug 09 2011
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On Wed, 10 Aug 2011 00:57:17 +0300, Jonathan M Davis <jmdavisProg gmx.com>
 
 wrote:
 I see having a compiler flag is a bad idea, it would be entirely in the
 source code.
 It is not about having more/less compatible compilers. There is only one
 compiler to be maintained and it is D.
 Getting rid of such a restriction (C compatibility) would be priceless i
 can't express, and i don't understand why it is not obvious.
 Our problem is this very restriction. D should be called "C like (easy
 to
 learn if you know C/C++) and supports C natively", not "C compatible".
 Because if being out of date was the only thing wrong about C then
 someone
 would just add some modern features and call it maybe... C++-+%?
In general, it's not a problem at all. It's only relevant when the C/C++ code is valid D code. It often isn't. For the most part, the code which is both as well (the primary exception that I can think of being pointers). It's stuff which is common across C-based languages and would screw over programmers pretty thoroughly if it changed. There are a few exceptions (such as this issue with the shift operator) which are a problem, but for the most part, it's really not an issue. It's trying to maintain C++'s level of compatibility with C where you really get into trouble. D does _not_ maintain anything even close to that level of compatability. It just chooses to be compatible when the syntax is identical so that code which is ported from C or C++ doesn't silently break.
What you are saying just covers the language being a C like language, aren't we talking about the restriction and threat it pose to the language?
 D breaks compatibility all over the place. It just doesn't do it when C
 code would be valid D code.
I am afraid this is not quite right. Wouldn't it be oxymoron when you reject every solution/idea using this argument? It doesn't matter if the idea has no merit or it was the meaning of life.
??? One of the rules of language design that Walter has stuck to throughout the development of D is that if you take any C or C++ code and try to compile it as D code, it will either fail to compile, or it will compile with the same behavior as the code would have had in C or C++. This is so that you don't silently introduce bugs when porting code from C or C++ to D. There's _lots_ of stuff in D that isn't valid C code, and there's plenty of stuff in C which isn't valid D code. Saying that C/C++ code must either be valid D code with the same behavior or not compile is not particularly restrictive on the whole. It's really only an issue with some arithmetic stuff. D is free to add additional restrictions (such as requiring casts for narrowing conversions), and it can do whatever it wants which is different from C as long as it isn't valid C - and almost everything that D does is different enough that it's almost a non-issue. You'd actually probably have a hard time trying to come up with a feature to introduce to the language which would be valid C but have different behavior in D than C. The common stuff is the really basic stuff like arithmetic and pointers, which isn't really where you're going to be adding features. The only issue with it really is that there are a few rough edges in C with regards to arithmetic. Anyone looking for a drastically different way of handling math between different integer types, for instance, is going to be out of luck. But there's not much beyond that which is strictly compatible with C. D is just too different from C for that small amount of compatibility to be much of an issue. It does crop up periodically but not often, and it's a whole world of difference from how C++ treats compatibility with C. - Jonathan M Davis
Aug 09 2011
parent so <so so.xn--s-fka> writes:
On Wed, 10 Aug 2011 01:36:58 +0300, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 ??? One of the rules of language design that Walter has stuck to  
 throughout
 the development of D is that if you take any C or C++ code and try to  
 compile
 it as D code, it will either fail to compile, or it will compile with  
 the same
 behavior as the code would have had in C or C++. This is so that you  
 don't
 silently introduce bugs when porting code from C or C++ to D.
I like Walter's ideas on language design and his analysis on other languages, this is not one of them. I don't understand what purpose it does serve.
 There's _lots_ of stuff in D that isn't valid C code, and there's plenty  
 of
 stuff in C which isn't valid D code. Saying that C/C++ code must either  
 be
 valid D code with the same behavior or not compile is not particularly
 restrictive on the whole.
You are missing the point. You'd be right if D being C like was a strange consequence (we did everything as it should be done, did not repeat other languages mistakes, funny enough a C code compiles like it is a D code) rather than the result of some restriction (which would have only one outcome) in design.
 It's really only an issue with some arithmetic
 stuff. D is free to add additional restrictions (such as requiring casts  
 for
 narrowing conversions), and it can do whatever it wants which is  
 different
 from C as long as it isn't valid C - and almost everything that D does is
 different enough that it's almost a non-issue.
Free to add but on what criteria?
 You'd actually probably have a hard time trying to come up with a  
 feature to
 introduce to the language which would be valid C but have different  
 behavior
 in D than C. The common stuff is the really basic stuff like arithmetic  
 and
 pointers, which isn't really where you're going to be adding features.
Funny enough it wouldn't be that hard find, my first appearance in this list was exactly on this issue. Remember there was no templates in C, and C numerics was designed for that environment which is far from D. Separation would be great help but i am afraid it is getting late for D. We know the consensus for major changes, especially at this stage.
Aug 09 2011
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/9/2011 2:08 PM, Jonathan M Davis wrote:
 From what I can see though, it sure looks like any C code which relies on the
 C behavior would be buggy.
I don't see any way to make that judgment. It's been Standard C behavior forever, including K+R prehistory. It's guaranteed that a lot of code will rely on it, whether or not anyone considers it good form.
Aug 09 2011
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On 8/9/2011 2:08 PM, Jonathan M Davis wrote:
 From what I can see though, it sure looks like any C code which relies on
 the C behavior would be buggy.
I don't see any way to make that judgment. It's been Standard C behavior forever, including K+R prehistory. It's guaranteed that a lot of code will rely on it, whether or not anyone considers it good form.
Hmmm. It seems to me that the question is whether anyone would ever _intentially_ use this behavior. Is there any benefit to it whatsoever? If not, I'd argue that the risk of breakage to C code being ported over is minimal and that the behavior should be fixed in D. Now, if there _is_ a benefit to it such that someone would use it intentionally, then that's another story, but it sure likes like the only reason that it would occur in C code would be because the programmer who wrote it screwed up. Now, that's still going to change the behavior of any code which used this behavior - even if it was by accident - which risks making code fail to work even if it only worked through a miracle before (i.e. in spite of the bug). So, maybe that's enough that we can't fix it. But it sure seems like the risk of new breakage is very low, since code whose behavior would change was broken in the first place, and I'd hate to see D's behavior stay like this if we can fix it. - Jonathan M Davis
Aug 09 2011
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/9/2011 3:04 PM, Jonathan M Davis wrote:
 Hmmm. It seems to me that the question is whether anyone would ever
 _intentially_ use this behavior. Is there any benefit to it whatsoever? If
 not, I'd argue that the risk of breakage to C code being ported over is
 minimal and that the behavior should be fixed in D. Now, if there _is_ a
 benefit to it such that someone would use it intentionally, then that's
 another story, but it sure likes like the only reason that it would occur in C
 code would be because the programmer who wrote it screwed up.

 Now, that's still going to change the behavior of any code which used this
 behavior - even if it was by accident - which risks making code fail to work
 even if it only worked through a miracle before (i.e. in spite of the bug).
 So, maybe that's enough that we can't fix it. But it sure seems like the risk
 of new breakage is very low, since code whose behavior would change was broken
 in the first place, and I'd hate to see D's behavior stay like this if we can
 fix it.
I don't see why it's a miracle that s>>1 would yield an int result. Do any of the C lints flag this as a bad practice?
Aug 09 2011
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On 8/9/2011 3:04 PM, Jonathan M Davis wrote:
 Hmmm. It seems to me that the question is whether anyone would ever
 _intentially_ use this behavior. Is there any benefit to it whatsoever?
 If not, I'd argue that the risk of breakage to C code being ported over
 is minimal and that the behavior should be fixed in D. Now, if there
 _is_ a benefit to it such that someone would use it intentionally, then
 that's another story, but it sure likes like the only reason that it
 would occur in C code would be because the programmer who wrote it
 screwed up.
 
 Now, that's still going to change the behavior of any code which used
 this behavior - even if it was by accident - which risks making code
 fail to work even if it only worked through a miracle before (i.e. in
 spite of the bug). So, maybe that's enough that we can't fix it. But it
 sure seems like the risk of new breakage is very low, since code whose
 behavior would change was broken in the first place, and I'd hate to see
 D's behavior stay like this if we can fix it.
I don't see why it's a miracle that s>>1 would yield an int result. Do any of the C lints flag this as a bad practice?
Then I'm misunderstanding something here. It was my understanding that this was yielding results which pretty much any programmer would consider incorrect or undesirable. As such, when this issue pops up, it yields an incorrect result, so the code is broken, and if it works, it's because something else gets around the issue (hence the miracle). If this isn't actually yielding effectively incorrect results, then it's a different issue entirely. So, I'm obviously misunderstanding exactly what the issue is - probably at least in part because I rarely need to use bitshifts. - Jonathan M Davis
Aug 09 2011
parent Timon Gehr <timon.gehr gmx.ch> writes:
Jonathan M Davis wrote:
 Walter Bright wrote:
 On 8/9/2011 3:04 PM, Jonathan M Davis wrote:
 Hmmm. It seems to me that the question is whether anyone would ever
 _intentially_ use this behavior. Is there any benefit to it whatsoever?
 If not, I'd argue that the risk of breakage to C code being ported over
 is minimal and that the behavior should be fixed in D. Now, if there
 _is_ a benefit to it such that someone would use it intentionally, then
 that's another story, but it sure likes like the only reason that it
 would occur in C code would be because the programmer who wrote it
 screwed up.

 Now, that's still going to change the behavior of any code which used
 this behavior - even if it was by accident - which risks making code
 fail to work even if it only worked through a miracle before (i.e. in
 spite of the bug). So, maybe that's enough that we can't fix it. But it
 sure seems like the risk of new breakage is very low, since code whose
 behavior would change was broken in the first place, and I'd hate to see
 D's behavior stay like this if we can fix it.
I don't see why it's a miracle that s>>1 would yield an int result. Do any of the C lints flag this as a bad practice?
Then I'm misunderstanding something here. It was my understanding that this >
was yielding results which pretty much any programmer would consider
 incorrect or undesirable. As such, when this issue pops up, it yields an
incorrect
 result, so the code is broken, and if it works, it's because something else
 gets around the issue (hence the miracle). If this isn't actually yielding
 effectively incorrect results, then it's a different issue entirely.

 So, I'm obviously misunderstanding exactly what the issue is - probably at
 least in part because I rarely need to use bitshifts.

 - Jonathan M Davis
I think what can be surprising is that s >>> 1 sign-extends s prior to the unsigned right shift. void main() { short s=-1; assert(s>>>1 == 2147483647); s=-1; s>>>=1; writeln(s == 32767); }
Aug 09 2011
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
so:

 Can't we finally get rid of this compatibility issue by separating?
 . for the whole file which can be enabled with a flag to compiler "dmd  
 -Cmode ...."
See: http://d.puremagic.com/issues/show_bug.cgi?id=4580 Bye, bearophile
Aug 09 2011
prev sibling next sibling parent KennyTM~ <kennytm gmail.com> writes:
On Aug 10, 11 01:06, Walter Bright wrote:
 On 8/9/2011 2:46 AM, Don wrote:
 From a discussion on D.learn.

 If x and y are different integral types, then in an expression like
 x >> y
 the integral promotion rules are applied to x and y.
 This behaviour is obviously inherited from C, but why did C use such a
 counter-intuitive and bug-prone rule?
 Why isn't typeof(x >> y) simply typeof(x) ?
 What would break if it did?

 You might think the the rule is that typeof( x >> y) is typeof( x + y),
 but it isn't: the arithmetic conversions are NOT applied:
 typeof(int >> long) is int, not long, BUT
 typeof(short >> int) is int.
 And we have this death trap (bug 2809):

 void main()
 {
 short s = -1;
 ushort u = s;
 assert( u == s );
 assert ( (s >>> 1) == (u >>> 1) ); // FAILS
 }
That last is why we can't just change the behavior from C.
Does C or C++ even have a '>>>' operator? If we need to have a type-promotion rule like C, it could be made as x >>> y == cast(promoted type) cast(typeof(x)) (unsigned(x) >> y) e.g. cast(short)(-1) >>> 1 == 0x7f.
Aug 10 2011
prev sibling parent Don <nospam nospam.com> writes:
Walter Bright wrote:
 On 8/9/2011 2:46 AM, Don wrote:
  From a discussion on D.learn.

 If x and y are different integral types, then in an expression like
 x >> y
 the integral promotion rules are applied to x and y.
 This behaviour is obviously inherited from C, but why did C use such a
 counter-intuitive and bug-prone rule?
 Why isn't typeof(x >> y) simply typeof(x) ?
 What would break if it did?

 You might think the the rule is that typeof( x >> y) is typeof( x + y),
 but it isn't: the arithmetic conversions are NOT applied:
 typeof(int >> long) is int, not long, BUT
 typeof(short >> int) is int.
 And we have this death trap (bug 2809):

 void main()
 {
 short s = -1;
 ushort u = s;
 assert( u == s );
 assert ( (s >>> 1) == (u >>> 1) ); // FAILS
 }
That last is why we can't just change the behavior from C.
???? C doesn't even have the >>> operator. There is no valid "backwards compatibility with C" argument here. I cannot see ANY justification whatsoever for the behaviour of >>>. Applying integral promotion to >>> is completely wrong. It is a bug in 100% of cases. Possibilities are: (1) use typeof( x >>> y) is typeof(x) (2) as (1), but break with C, and apply the same rule for typeof(x>>y) (The C rule isn't too unreasonable for x << y). (3) generate a compile error for x >>> y when x is short or byte. Actually, I found that I misread the spec. It actually says: "The operands must be integral types, and undergo the usual integral promotions. The result type is the type of the left operand after the promotions. The result value is the result of shifting the bits by the right operand's value." So with x >>> y, the type of y actually has no effect; if x is smaller than int, the result is int; otherwise, the type is x. So in fact, if x is short or byte, then x >>> y is NEVER an unsigned shift, regardless of what y is: it's always the same as x >> y. But the spec says >>> performs an unsigned shift. This disagrees with the >>>= operator as well: >>>= always performs an unsigned shift, even though the spec says that x >>>= y is the same as x = x >>> y. Given that >>> is the same as >> for any unsigned type
Aug 10 2011