Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
October 26, 2020 Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
For those who don't know, the current compiler does not integer promote for negation. This means that typeof(-x) is typeof(x). This also means that for things like short and byte: short x = short.min; // -32768 int y = -x; // still -32768 ulong z = -x; // 18446744073709518848 In C (and in common sense), y and z are 32768. So we have a new system, which integer-promotes a value from short or byte to int *before* the negation. However, this perfectly valid code will no longer work: short x2 = -x; Instead, you must *cast* the -x to short (and to get rid of the deprecation, you must also cast the x to int before using - on it). The annoying thing here is, this *buys you nothing*. Before the new integer promotion rules, if x was short.min, x2 is short.min. After the new integer promotion rules, with a cast, x2 is *still* short.min. I think this one problem is going to cause us to never turn on the intpromote rule by default. It will be insanely disruptive. I have an idea to make this better. The true cases where integer promotion is a problem is when you use the negation of a smaller type implicitly as a larger type. Other than that, the differing rules produce the same results. I propose that the VRP state has a flag added indicating that the range came from a negation directly (a cast or any other operations will eliminate the flag). In the case where a value of a larger type VRP range is assigned to a value that holds all of the range EXCEPT the topmost value, and the negation flag is set, the assignment is allowed. Otherwise, if it is assigned to a larger type, and the type's minimum is in the VRP, then trigger the deprecation. How this might look: short x2 = -x; Without -preview=intpromote: -x has type short with VRP range short.min to short.max inclusive, and negation flag set. Assigning to a short works, because the VRP allows it. No warnings/errors. With -preview=intpromote: -x has type int with VRP range short.min + 1 to short.max + 1 inclusive, and negation flag set. Assigning to a short is allowed because of the new rule (if negation was involved, then it's allowed to assign if the upper range is only one higher than allowed). int i = -x; Without -preview=intpromote: -x has type short with VRP range short.min to short.max inclusive, and negation flag set. Assigning to an int works, but triggers the deprecation because of the negation flag, type being promoted implicitly, and the VRP includes short.min. With -preview=intpromote: -x has type int with VRP range short.min + 1 to short.max + 1 inclusive, and negation flag set. Assigning to an int works, because int can hold all the values. Does this make sense? I think it makes for a lot better targeted diagnostic and problem fix. -Steve |
October 26, 2020 Re: Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Monday, 26 October 2020 at 13:06:09 UTC, Steven Schveighoffer wrote:
> For those who don't know, the current compiler does not integer promote for negation. This means that typeof(-x) is typeof(x). This also means that for things like short and byte:
>
> short x = short.min; // -32768
> int y = -x; // still -32768
> ulong z = -x; // 18446744073709518848
>
> In C (and in common sense), y and z are 32768.
>
> So we have a new system, which integer-promotes a value from short or byte to int *before* the negation.
>
> However, this perfectly valid code will no longer work:
>
> short x2 = -x;
The obvious solution is to not allow modular arithmetics for signed integers (like most languages) and trap on overflow in debug builds.
I really dislike the whole idea of using VRP for typing. The code should behave the same way whether the compiler can figure out the value or not. Such special casing will lead to problems down the road.
|
October 27, 2020 Re: Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Monday, 26 October 2020 at 13:06:09 UTC, Steven Schveighoffer wrote:
> [snip]
>
> -Steve
Wouldn't it be simpler if integer promoting expressions were implicitly castable to types they were promoted from, meaning:
```
short a = 1, b = 2;
short c = a+b; //fine, int promoted from short implicitly castable back
auto i = a+b; //int, because the type is still int if not casted
short d = cast(short)i; //cast required here, promotion reversibility applies only for expressions
byte e = cast(byte)(a+b); //cast required because byte is smaller than short
```
Won't solve both your problem and a whole host of others with `byte`s and `short`s?
It should not be too hard to implement either, as array literals already do the same. They are implicitly castable to static arrays of their own size, while their type (dynamic array) is not.
|
October 28, 2020 Re: Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dukc | On 10/27/20 7:13 PM, Dukc wrote:
> On Monday, 26 October 2020 at 13:06:09 UTC, Steven Schveighoffer wrote:
>> [snip]
>>
>
> Wouldn't it be simpler if integer promoting expressions were implicitly castable to types they were promoted from, meaning:
>
> ```
> short a = 1, b = 2;
> short c = a+b; //fine, int promoted from short implicitly castable back
> auto i = a+b; //int, because the type is still int if not casted
> short d = cast(short)i; //cast required here, promotion reversibility applies only for expressions
> byte e = cast(byte)(a+b); //cast required because byte is smaller than short
> ```
>
> Won't solve both your problem and a whole host of others with `byte`s and `short`s?
>
> It should not be too hard to implement either, as array literals already do the same. They are implicitly castable to static arrays of their own size, while their type (dynamic array) is not.
I don't know if this is OK or correct. For example
short c = a * b; => lots of truncation gonna happen.
Note that C allows this, and D does not, on purpose.
I wanted to focus on the problem that *currently* compiles, will *not* compile after the change, and *behaves exactly the same* even with mitigation (casting). In other words, we would require lots of mindless busywork (and/or break lots of old code) for zero benefit.
Essentially, I like the idea of the intpromote preview. But this one problem is going to derail its adoption.
-Steve
|
October 28, 2020 Re: Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Wednesday, 28 October 2020 at 14:52:28 UTC, Steven Schveighoffer wrote:
>
> I don't know if this is OK or correct. For example
>
> short c = a * b; => lots of truncation gonna happen.
This sort of thing is still allowed with -preview=intpromote, for types that are int-sized or larger:
int a = int.max, b = int.max;
int c = a*b; // compiles and truncates
The most principled way to approach this is to either *always* require a cast for possibly-truncating operations, or to *never* require one. As it stands, `-preview=intpromote` is an unsatisfying half-measure that both requires casts when they are not necessary, and fails to require them when they may be necessary.
|
October 28, 2020 Re: Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
Posted in reply to Paul Backus | On 10/28/20 11:13 AM, Paul Backus wrote: > On Wednesday, 28 October 2020 at 14:52:28 UTC, Steven Schveighoffer wrote: >> >> I don't know if this is OK or correct. For example >> >> short c = a * b; => lots of truncation gonna happen. > > This sort of thing is still allowed with -preview=intpromote, for types that are int-sized or larger: > > int a = int.max, b = int.max; > int c = a*b; // compiles and truncates Yeah, I never liked that aspect of integer promotion. In terms of practicality, int is probably a reasonable place to limit this for addition (it's very rare for adding 2 integers lead to an overflow, much less so than 2 shorts), but not multiplication. > The most principled way to approach this is to either *always* require a cast for possibly-truncating operations, or to *never* require one. As it stands, `-preview=intpromote` is an unsatisfying half-measure that both requires casts when they are not necessary, and fails to require them when they may be necessary. That ship may have sailed though. The goal is also different. I want to *avoid* breaking existing code, except in the case where it differs from the expected behavior. The exception in all of this is -int.min, which is still not going to work right. I also wonder how much D coders would tolerate such rules. I've used more restrictive languages which don't allow implicit conversions in many cases between integer types, and it can be painful. -Steve |
October 28, 2020 Re: Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Wednesday, 28 October 2020 at 17:34:10 UTC, Steven Schveighoffer wrote:
> That ship may have sailed though. The goal is also different. I want to *avoid* breaking existing code, except in the case where it differs from the expected behavior. The exception in all of this is -int.min, which is still not going to work right.
Just add a switch that compiles with runtime overflow checks for signed integers (that prints file and line number when it traps). Then run it on available codebases and get an impression of where the issues are.
|
October 28, 2020 Re: Make -preview=intpromote not suck | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Wednesday, 28 October 2020 at 17:34:10 UTC, Steven Schveighoffer wrote: > That ship may have sailed though. The goal is also different. I want to *avoid* breaking existing code, except in the case where it differs from the expected behavior. The exception in all of this is -int.min, which is still not going to work right. My idea should not break any code, because for every expression that compiles now the type is still going to be the same. That's exactly why I want short+short to still be int if not assigned back to short. > > I also wonder how much D coders would tolerate such rules. I've used more restrictive languages which don't allow implicit conversions in many cases between integer types, and it can be painful. The problem is not casting strictness in general. If I'm dealing with 32-bit variables, I agree that I want them to trunctate only via an explicit cast. The problem is the implicit integer promotion, when I'm not using any 32 or 64-bit. I do not want to explicitly cast every single expression back to the original type just because my arithmetic variables have shorter than normal lengths. That is --very-- ugly. |
Copyright © 1999-2021 by the D Language Foundation