View mode: basic / threaded / horizontal-split · Log in · Help
September 19, 2011
Incomplete knowledge + all powerful casts = troubles
This is a simplified version of some older D code of mine.
The cast is needed to convert int->uint:


uint foo(T)(T x) if (is(T == uint)) {
   uint ans = 0;
   while (x >>= 1)
       ans++;
   return ans;
}
void bar(int x) {
   auto xx = foo(cast(uint)x);
}
void main() {}



I have "improved" it adding a nice "in" in the bar() signature. The code compiles and runs still:


uint foo(T)(T x) if (is(T == uint)) {
   uint ans = 0;
   while (x >>= 1)
       ans++;
   return ans;
}
void bar(in int x) {
   auto xx = foo(cast(uint)x);
}
void main() {}


Unfortunately now the code contains a small bug. The cast now converts const(int)->uint, and foo() will modify a const value, that gives undefined effects in D2.

D1 has only "const" (that is "enum" of D2), so I think a single kind of cast() is enough in D1. But D2 has const/immutable too (and other tags) so I think a single kind of cast is now a bit short.

If Phobos (or D2 itself) adds a kind of const_cast!(), _and_ the power of D cast() is reduced removing its ability to remove attributes like const/immutable from a type, the bug I have created doesn't happen, but it looks a bit messy:

const(int) x;
auto y = cast(uint)x;
static assert(is(typeof(y) == const(uint)));
auto z = const_cast!uint(x);
static assert(is(typeof(z) == uint));


What do you think?

A cast means punching a hole in the type system, so the programmer has to use it with care. The problems are born from the programmer not having a complete knowledge of the situation (the types involved) when he/she uses the cast, or as in this case when I have later modified the code.

The example shown in this post is only the last one I have found. Casts now and then cause me small troubles.

This is not the first time we discuss about casts, and probably it isn't the last one :-)

Bye,
bearophile
September 19, 2011
Re: Incomplete knowledge + all powerful casts = troubles
On Mon, 19 Sep 2011 13:23:32 -0400, bearophile <bearophileHUGS@lycos.com>  
wrote:

> This is a simplified version of some older D code of mine.
> The cast is needed to convert int->uint:
>
>
> uint foo(T)(T x) if (is(T == uint)) {
>     uint ans = 0;
>     while (x >>= 1)
>         ans++;
>     return ans;
> }
> void bar(int x) {
>     auto xx = foo(cast(uint)x);
> }
> void main() {}
>
>
>
> I have "improved" it adding a nice "in" in the bar() signature. The code  
> compiles and runs still:
>
>
> uint foo(T)(T x) if (is(T == uint)) {
>     uint ans = 0;
>     while (x >>= 1)
>         ans++;
>     return ans;
> }
> void bar(in int x) {
>     auto xx = foo(cast(uint)x);
> }
> void main() {}
>
>
> Unfortunately now the code contains a small bug. The cast now converts  
> const(int)->uint, and foo() will modify a const value, that gives  
> undefined effects in D2.

While I agree D2 needs a const_cast, this code is still correct.  uints  
are passed by value, so it is fine what you did.

I'd say to avoid needing the cast, you should instead change bar to this:

auto xx = foo!uint(x);

which should work just fine, no cast needed.

-Steve
September 19, 2011
Re: Incomplete knowledge + all powerful casts = troubles
> Unfortunately now the code contains a small bug. The cast now converts  
> const(int)->uint, and foo() will modify a const value, that gives  
> undefined effects in D2.

I get the point, but in this particular case it's not a real bug since foo  
doesn't take it by ref.
The int is copied anyways, no matter if the source is const or not.

I'm not sure how the actual problem should be solved though...

> const(int) x;
> auto y = cast(uint)x;
> static assert(is(typeof(y) == const(uint)));

...cause this doesn't look right either, cause it silently keeps the const.
Imagine x being unapparently const and you pass y to some function or  
template that doesn't allow const.
You get an abstruse error message and don't really know why it happened  
cause you think y is just a uint.

Maybe stripping away constness should be a special case that can't be  
combined with a normal cast, i.e. only 'cast()' can do it.
September 19, 2011
Re: Incomplete knowledge + all powerful casts = troubles
On Mon, 19 Sep 2011 13:54:20 -0400, Trass3r <un@known.com> wrote:

>> Unfortunately now the code contains a small bug. The cast now converts  
>> const(int)->uint, and foo() will modify a const value, that gives  
>> undefined effects in D2.
>
> I get the point, but in this particular case it's not a real bug since  
> foo doesn't take it by ref.
> The int is copied anyways, no matter if the source is const or not.
>
> I'm not sure how the actual problem should be solved though...
>
>> const(int) x;
>> auto y = cast(uint)x;
>> static assert(is(typeof(y) == const(uint)));
>
> ...cause this doesn't look right either, cause it silently keeps the  
> const.
> Imagine x being unapparently const and you pass y to some function or  
> template that doesn't allow const.
> You get an abstruse error message and don't really know why it happened  
> cause you think y is just a uint.
>
> Maybe stripping away constness should be a special case that can't be  
> combined with a normal cast, i.e. only 'cast()' can do it.

The way it's done in C++, you cannot remove const without a C-style cast  
or a const_cast.  The above would simply be a compiler error (instead of  
silently doing what you didn't ask it to do).

-Steve
September 19, 2011
Re: Incomplete knowledge + all powerful casts = troubles
On Mon, 19 Sep 2011 14:01:02 -0400, Steven Schveighoffer  
<schveiguy@yahoo.com> wrote:

> On Mon, 19 Sep 2011 13:54:20 -0400, Trass3r <un@known.com> wrote:
>
>>> Unfortunately now the code contains a small bug. The cast now converts  
>>> const(int)->uint, and foo() will modify a const value, that gives  
>>> undefined effects in D2.
>>
>> I get the point, but in this particular case it's not a real bug since  
>> foo doesn't take it by ref.
>> The int is copied anyways, no matter if the source is const or not.
>>
>> I'm not sure how the actual problem should be solved though...
>>
>>> const(int) x;
>>> auto y = cast(uint)x;
>>> static assert(is(typeof(y) == const(uint)));
>>
>> ...cause this doesn't look right either, cause it silently keeps the  
>> const.
>> Imagine x being unapparently const and you pass y to some function or  
>> template that doesn't allow const.
>> You get an abstruse error message and don't really know why it happened  
>> cause you think y is just a uint.
>>
>> Maybe stripping away constness should be a special case that can't be  
>> combined with a normal cast, i.e. only 'cast()' can do it.
>
> The way it's done in C++, you cannot remove const without a C-style cast  
> or a const_cast.  The above would simply be a compiler error (instead of  
> silently doing what you didn't ask it to do).

Except, of course, the above is not even a valid test case :)  No cast is  
needed, this should work (today):

const(int) x;
uint y = x;

A more relevant case:

const(int) * x;
auto y = cast(uint *)x; // should be an error
auto z = cast(const(uint) *)x; // ok, const isn't being removed.

-Steve
Top | Discussion index | About this forum | D home