June 14, 2019
On 14.06.19 02:41, Exil wrote:
> On Thursday, 13 June 2019 at 21:18:16 UTC, Timon Gehr wrote:
>> On 13.06.19 22:55, Exil wrote:
>>> On Thursday, 13 June 2019 at 19:21:53 UTC, Tim wrote:
>>>> On Tuesday, 11 June 2019 at 00:30:27 UTC, Walter Bright wrote:
>>>>> If you want @safe to mean "no undefined behavior", that is a valid opinion, but that is not what @safe in D is currently defined as. It is currently defined as "memory safe". If you can find a case where an int with garbage in it can cause memory corruption in @safe code, that would indeed be a bug in D.
>>>>
>>>> The following code causes memory corruption, because a bool has garbage in it.
>>>>
>>>> import std.stdio;
>>>>
>>>> // Returns newly allocated data with increased value if b is true.
>>>> // Returns same data otherwise.
>>>> const(int)* test(immutable int *data, bool b) @trusted
>>>> {
>>>>     int *data2;
>>>>     if(!b)
>>>>     {
>>>>         writeln("b seems to be false");
>>>>         // The cast can be trusted, because data2 will only be modified if b is true.
>>>>         data2 = cast(int*)data;
>>>>     }
>>>>     else
>>>>     {
>>>>         writeln("b seems to be true");
>>>>         data2 = new int(*data);
>>>>     }
>>>>
>>>>     if(b)
>>>>     {
>>>>         writeln("b seems to be true");
>>>>         *data2 += 1;
>>>>     }
>>>>     return data2;
>>>> }
>>>>
>>>> void main() @safe
>>>> {
>>>>     bool b = void;
>>>>     immutable int *data = new immutable int(1);
>>>>     writeln("data before: ", *data);
>>>>     const int *data2 = test(data, b);
>>>>     writeln("data after: ", *data);
>>>>     writeln("data2 after: ", *data2);
>>>> }
>>>>
>>>> After compiling it with dmd and running it I get the following output:
>>>> data before: 1
>>>> b seems to be false
>>>> b seems to be true
>>>> data after: 2
>>>> data2 after: 2
>>>>
>>>> The immutable integer pointed to by data is modified. The function test is @trusted, but only modifies the immutable data, because b seems to be true and false at the same time.
>>>> Normally a bool is internally 0 or 1 and the compiler can assume that. If b has another value and !b is implemented as ~b, then b and !b can both be true.
>>>>
>>>> This means, that uninitialized data can result in memory corruption.
>>>
>>> This problem happens because you are used @trusted. If you used @safe you wouldn't be able to increment pointers and modify the values the way you did in @trusted.
>>
>> What that code illustrates is that `void` initialization leads to boolean values that are both `true` and `false` at the same time.
>> The @trusted function in the example can't do anything bad assuming that `b` is either true or false. If `void` initialization is @safe, @trusted functions can no longer assume that boolean parameters cannot be `true` and `false` at the same time. What's more, neither can @safe functions.
>>
>> In my book, an optimizer that can't elide the `if(b)` in the above code is a bad optimizer. Therefore either `void` initialization should not be @safe, or `void` initialization of a bool should be guaranteed to result in a value that is either true or false, but not both.
> 
> They aren't both true and false at the same time. Merely the code that is generated seems to be most likely checking that it is 1.

No. Tim explained _exactly_ what is going on. Please read before responding.

> This is more a bug with the compiler, nothing more.

It seems perfectly plausible that `void` initialization destroys invariants of types other than booleans, and that this destruction of invariants will lead to similar memory corruption problems.

Changing the compiler such that booleans do not have an invariant only fixes this instance of the problem, it does not address the general problem with `void` initialization in @safe code that it is a symptom of.

> It should only be checking if the value is zero, anything else should be true.

That's precisely what it is doing, but it uses bitwise negation to flip the value of the bool. So both b and !b are not equal to zero.
June 14, 2019
On 14.06.19 03:17, Exil wrote:
> On Friday, 14 June 2019 at 01:12:21 UTC, Timon Gehr wrote:
>> On 14.06.19 02:23, Exil wrote:
>>> On Thursday, 13 June 2019 at 21:26:37 UTC, Tim wrote:
>>>> On Thursday, 13 June 2019 at 20:55:34 UTC, Exil wrote:
>>>>> This problem happens because you are used @trusted. If you used @safe you wouldn't be able to increment pointers and modify the values the way you did in @trusted.
>>>>
>>>> Here is a completly @safe version:
>>>>
>>>> import std.stdio;
>>>>
>>>> static int[2] data;
>>>> static int[253] data2;
>>>>
>>>> void test(bool b) @safe
>>>> {
>>>>     data[b]++;
>>>> }
>>>>
>>>> void main() @safe
>>>> {
>>>>     bool b = void;
>>>>     writeln(data, data2);
>>>>     test(b);
>>>>     writeln(data, data2);
>>>> }
>>>>
>>>> If b is valid only data can change. But for me data2 changes, even though it is never written to.
>>>
>>> This is a bug.
>>
>> Yes. And the bug is either
>> - that `void` initialization of `bool` is `@safe`.
>> - that `void` initialization of `bool` can produce a value that is both `true` and `false`.
>> - that boolean values are assumed to be either `true` or `false` in @safe code.
>>
>> Which one seems most plausible to you?
> 
> None of them. Code generation is incorrect for boolean values.
> ...

That's the second option above... And I already explained why that answer is not satisfactory.

>>> It seems it doesn't do bounds checking for the index because it is a bool value and it is less than the static type. If you change the array to a dynamically allocated one, an assert is hit as expected.
>>
>> That's not expected, this is just the compiler not being as smart as it could be given the available information.
> 
> A value is used that is out of bounds of the array, yes that assert is expected.

The compiler is able to derive that it is not out of bounds...
June 14, 2019
On 13.06.19 23:23, Timon Gehr wrote:
>>
> 
> Basically, it could be argued that `void` initialization of a `bool` variable is already memory corruption. It's indistinguishable from someone holding a dangling `char*` to your `bool` memory and then writing a value other than '\0' or '\xff'.

It seems that Tim's explanation with ~ wasn't exactly what's actually going on and that it is rather x^1, so this should be "other than '\0' or '\1'".
June 14, 2019
On 13.06.19 23:49, Walter Bright wrote:
> On 6/13/2019 1:25 PM, Timon Gehr wrote:
>> On 13.06.19 09:27, Walter Bright wrote:
>>> The spec's wrong, because it doesn't do that.
>>
>> Memory safety implies no undefined behavior. The only way the spec can be wrong [1] is if you say that corrupting memory has defined behavior in D, in which case the spec would be too weak, and not too strong like you are implying. Otherwise, "memory safe" and "no undefined behavior" are equivalent.
> 
> I've already agreed that an uninitialized int should have an undefined value, not undefined behavior :-)
> ...

To me it looked like you first (02:37) agreed that the spec is fine, but then later (09:27) asserted that the spec is wrong. This might just be a case of Walter time travel though.

> The spec does indeed lack precision with these terms, and it's something well worth addressing.
> 
> 
>> [1] Assuming here that we accept that @safe successfully protects against memory corruption, ignoring assumptions that need to be made on @trusted functions.
> 
> The spec should be clear that @trusted functions must present an @safe interface.

I was just covering my bases. :)
June 14, 2019
On 14.06.19 01:30, Manu wrote:
> To be clear, I'm not disagreeing with Walter; I agree with him. I'm
> just pointing out that his position is inconsistent, and this quote is
> correct. But you have to accept that if this quote is correct, then `=
> void` shouldn't be @safe either.

Indeed. (If anyone is wondering: For some `struct S` with private members, `S s=void;` will produce an `s` whose private members contain garbage, which is not much better than just assigning values to them directly.)
June 14, 2019
On 14.06.19 02:15, Jonathan M Davis wrote:
> Well, I fail to see how allowing access to private data is not @safe, since
> it does not inherently mean that there are going to be problems with memory
> safety.

int[N] array;
static assert(0<N);

struct VerifiedIndex{
    private int index_=0;
    @disable this(int); // shouldn't be necessary, but it is
    @property int index()@safe{ return index_; }
    @property void index(int i)@trusted{ // no, this can't be @safe
        enforce(0<=x&&x<N, "index out of bounds");
        index_=i;
    }
    int read()@trusted{ return array.ptr[index]; }
    void write(int x)@trusted{ array.ptr[index]=x; }
}

This kind of thing is necessary. You need to give @trusted a chance to provide a @safe interface. This is very hard if all @safe code can randomly assign to your private members.
June 13, 2019
On 6/13/2019 5:35 PM, Timon Gehr wrote:
> It is obvious that allocation has to be pure. However, accessing the address of your data is impure. The runtime systems of pure functional languages also allocate memory, but the addresses are not exposed to the pure user code.

I know that technically you are correct, but I'm not so sure it matters for D's use of purity.
June 13, 2019
On 6/13/2019 6:05 PM, Timon Gehr wrote:
> If the result is mutable, there should be no common subexpression elimination because it affects aliasing, but for immutable results, the compiler should be allowed to pool identical values at its own leisure. (Note that this is not currently done.)

I've suspected for a long time that the value of pure functions is not CSE, but the knowledge that there aren't side effects. Side effects are the bane of understanding code.

June 13, 2019
On Thursday, June 13, 2019 9:22:48 PM MDT Walter Bright via Digitalmars-d wrote:
> On 6/13/2019 6:05 PM, Timon Gehr wrote:
> > If the result is mutable, there should be no common subexpression elimination because it affects aliasing, but for immutable results, the compiler should be allowed to pool identical values at its own leisure. (Note that this is not currently done.)
>
> I've suspected for a long time that the value of pure functions is not CSE, but the knowledge that there aren't side effects. Side effects are the bane of understanding code.

Given how restricted the situations are when you can elide function calls with pure functions (especially without doing code flow analysis), I don't see how it's really any benefit at all aside from maybe math code. IIRC, duplicate pure function calls currently only get elided within the same expression, but even if it were within the same statement, that sort of thing is pretty rare outside of code that's doing math.

On the other hand, knowing that a pure function doesn't access anything that it's not given is of value even if you have no strongly pure functions in your code anywhere.

So, I've defintely come to the conclusion that functional purity really isn't one of the benefits (let alone the primary benefit) of D's pure in practice.

- Jonathan M Davis



June 14, 2019
On 14.06.19 02:23, Timon Gehr wrote:
> On 14.06.19 00:27, Manu wrote:
[...]
>> Breaking private access is not in any way shape or form a memory
>> safety violation.
>>
> 
> It can very well be if the private data is managed by a @trusted interface that assumes @safe code cannot meddle with that data because it is private.

Strictly speaking, @trusted can't assume that. One can always write @safe code in the same module and (accidentally) mess with the private data that way.

An @trusted function that relies on `private` for safety does not present an @safe interface to its own module.

In practice, that's often overlooked or ignored, though. With a restricted tupleof, at least the damage is contained in one module.