Thread overview
A range analysis question
Jul 10, 2013
bearophile
Jul 10, 2013
Ali Çehreli
Jul 10, 2013
bearophile
Jul 10, 2013
Ali Çehreli
Jul 11, 2013
bearophile
Jul 11, 2013
bearophile
Jul 11, 2013
bearophile
July 10, 2013
Do you know why the assignment to 'item' is accepted in the first case and refused in the second?


ubyte generate1(s...)() {
    ubyte[10] result;
    foreach (immutable i, ref item; result)
        item = s[0][0] << 4;
    return result[0];
}

ubyte generate2(s...)() {
    ubyte[10] result;
    foreach (immutable i, ref item; result)
        item = s[0][i % 3] << 4; // line 11
    return result[0];
}

void main() {
    enum ubyte[16] data = [1, 2, 3, 4];
    auto g1 = generate1!data;
    auto g2 = generate2!data;
}


dmd gives:

test.d(11): Error: cannot implicitly convert expression (cast(int)[cast(ubyte)1u, cast(ubyte)2u, cast(ubyte)3u, cast(ubyte)4u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u][i % 3u] << 4) of type int to ubyte


Bye and thank you,
bearophile
July 10, 2013
On 07/10/2013 03:08 PM, bearophile wrote:

> Do you know why the assignment to 'item' is accepted in the first case
> and refused in the second?
>
>
> ubyte generate1(s...)() {
>      ubyte[10] result;
>      foreach (immutable i, ref item; result)
>          item = s[0][0] << 4;

s[0][0] is known to be int 1 at compile time. It is also known that (1 << 4) can fit in a ubyte by value range propagation. dmd is being helpful.

Change the code so that the result does not fit a ubyte you will get the same error as in line 11.

a) Shift by 8 instead of 4

b) Change the value in main to something like 42

>      return result[0];
> }
>
> ubyte generate2(s...)() {
>      ubyte[10] result;
>      foreach (immutable i, ref item; result)
>          item = s[0][i % 3] << 4; // line 11

That foreach is a runtime foreach because it is applied on 'result' as opposed to the type tuple 's'. Apparently the compiler does not do that kind of code analysis.

>      return result[0];
> }
>
> void main() {
>      enum ubyte[16] data = [1, 2, 3, 4];
>      auto g1 = generate1!data;
>      auto g2 = generate2!data;
> }
>
>
> dmd gives:
>
> test.d(11): Error: cannot implicitly convert expression
> (cast(int)[cast(ubyte)1u, cast(ubyte)2u, cast(ubyte)3u, cast(ubyte)4u,
> cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u,
> cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u,
> cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u, cast(ubyte)0u][i % 3u] <<
> 4) of type int to ubyte
>
>
> Bye and thank you,
> bearophile

Ali

July 10, 2013
Ali Çehreli:

> Change the code so that the result does not fit a ubyte you will get the same error as in line 11.

The result is supposed to fit in an ubyte because the input is supposed to be made of nibbles. (I have tried to add asserts or tests, but they don't change the situation, so I have omitted them here).


> That foreach is a runtime foreach because it is applied on 'result' as opposed to the type tuple 's'. Apparently the compiler does not do that kind of code analysis.

Right, but is it right to ask for such analysis to be performed?

Bye,
bearophile
July 10, 2013
On 07/10/2013 04:10 PM, bearophile wrote:

> Ali Çehreli:
>
>> Change the code so that the result does not fit a ubyte you will get
>> the same error as in line 11.
>
> The result is supposed to fit in an ubyte because the input is supposed
> to be made of nibbles.

Not in either of the two cases that I have given:

a) (1 << 8) would not fit a ubyte

b) (42 << 4) would not fit a ubyte

Of course we are reminded again that even if 1 and 42 are ubytes, the result of the << operator is int.

So you first expression (1 << 4) is an int which happens to fit a ubyte and that line compiles.

> (I have tried to add asserts or tests, but they
> don't change the situation, so I have omitted them here).
>
>
>> That foreach is a runtime foreach because it is applied on 'result' as
>> opposed to the type tuple 's'. Apparently the compiler does not do
>> that kind of code analysis.
>
> Right, but is it right to ask for such analysis to be performed?

That would be great but there are many other cases where the compiler does not do anything like that. Here, it would have to decide similar to "even though this is a runtime foreach, I know at compile time that 'result' is a fixed-length array so 'i' is between 0 and 10. I also have this compile-time tuple template parameters. Now let me see whether running that code would produce values that would fit the 'result's type ubyte."

I don't think we are there yet. :)

>
> Bye,
> bearophile

Ali

July 11, 2013
Ali Çehreli:

> That would be great but there are many other cases where the compiler does not do anything like that. Here, it would have to decide similar to "even though this is a runtime foreach, I know at compile time that 'result' is a fixed-length array so 'i' is between 0 and 10. I also have this compile-time tuple template parameters. Now let me see whether running that code would produce values that would fit the 'result's type ubyte."
>
> I don't think we are there yet. :)

Adding a bit more static analysis to D will help making D a bit more modern language. Having something like "liquid types" is very good, but I think that even much more limited capabilities are enough to avoid avoid some casts and bugs.

Recently I have added a related (simple) enhancement request, perhaps it's implementable:

http://d.puremagic.com/issues/show_bug.cgi?id=10594

Bye,
bearophile
July 11, 2013
Ali Çehreli:

> I don't think we are there yet. :)

Thinking some more about this topic I have created a small test example:


uint i = 100;
void main(in string[] args) {
    auto j = args.length;
    ubyte x1 = (i ^^ 2) % 256; // OK
    ubyte x2 = (i ^^ 3) % 256; // OK
    ubyte[] arr = [(i ^^ 2) % 256, (i ^^ 3) % 256]; // OK!
    ubyte y = [(i ^^ 2) % 256, (i ^^ 3) % 256][j]; // Error
}


The lines with x1 and x2 are accepted, because the range analysis is able to infer those are in-range assignments. While the assignment of y is refused, despite all the contents of the array can be inferred as castable to ubyte, as shown in assignment of arr.

I think this (the line with y) is worth an enhancement request.

Bye,
bearophile
July 11, 2013
> I think this (the line with y) is worth an enhancement request.

http://d.puremagic.com/issues/show_bug.cgi?id=10615

Bye,
bearophile