June 14, 2019
On Thursday, 13 June 2019 at 21:08:42 UTC, Timon Gehr wrote:
> On 13.06.19 22:20, Exil wrote:
>> On Thursday, 13 June 2019 at 19:51:12 UTC, Timon Gehr wrote:
>>> On 13.06.19 21:44, Walter Bright wrote:
>>>> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>>>>> Yes. IMHO this shouldn't be a @safe pure operation.
>>>>
>>>> D regards converting a pointer to an int as safe, but not the other way around.
>>>
>>> I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.
>> 
>> The operation is pure.
>
> No.

What global state is it accessing then? If you remove the new allocate and just use a passed in pointer parameter.

>> Accessing global state is the part that is not pure. For convience I guess you can allocate memory, ...
>
> It's an _abstraction_. You allocate because you need memory to represent your values, not because you want to know at which address your values will be located. The former is a necessity, the latter is not pure.
>
> Purely functional programming languages also allocate memory at runtime, but it is hidden in the runtime system and kept separate from the language definition. In D, user code and runtime system can both be in the same code base, but the runtime system parts need to be in impure or pure @system code, not in your pure code.

Yes, that is not pure. It's a compromise for usability.

>> ... > Pure for the most part guarantees you don't
>> access global variables.
>
> No, it does not. It guarantees you don't read or write implicit state (i.e. state not accessible through the function arguments).

Tomato tomato.

>> Pure doesn't mean deterministic.
>
> Yes, it does. A function can only fail to be deterministic if it accesses implicit state that changes between function invocations.

Which gets violated anyways because of the GC.


June 14, 2019
On 13.06.19 23:45, Walter Bright wrote:
> On 6/13/2019 12:51 PM, Timon Gehr wrote:
>> On 13.06.19 21:44, Walter Bright wrote:
>>> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>>>> Yes. IMHO this shouldn't be a @safe pure operation.
>>>
>>> D regards converting a pointer to an int as safe, but not the other way around.
>>
>> I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.
> 
> We had a discussion a while back about whether allocating memory via new was pure or not, since a different value was produced each time. We eventually took the pragmatic route that pure functions would be next to useless if this was not allowed.

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.
June 14, 2019
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. This is more a bug with the compiler, nothing more. It should only be checking if the value is zero, anything else should be true. Seems to change quite a lot looking at the output. But then that's DMD's backend for you, wouldn't hurt to add a test for this.




June 14, 2019
On Friday, 14 June 2019 at 00:35:10 UTC, Timon Gehr wrote:
> On 13.06.19 23:45, Walter Bright wrote:
>> On 6/13/2019 12:51 PM, Timon Gehr wrote:
>>> On 13.06.19 21:44, Walter Bright wrote:
>>>> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>>>>> Yes. IMHO this shouldn't be a @safe pure operation.
>>>>
>>>> D regards converting a pointer to an int as safe, but not the other way around.
>>>
>>> I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.
>> 
>> We had a discussion a while back about whether allocating memory via new was pure or not, since a different value was produced each time. We eventually took the pragmatic route that pure functions would be next to useless if this was not allowed.
>
> 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.

And you can't return the pointer to something in those languages. It is entirely dependent on the language, this is not a functional language. You can return pointers. What do you do when you expect two calls to have different allocated objects that have the same value? Does the compiler simply produce one object, or two or more?


June 14, 2019
On Friday, 14 June 2019 at 00:49:55 UTC, Exil wrote:
> On Friday, 14 June 2019 at 00:35:10 UTC, Timon Gehr wrote:
>> On 13.06.19 23:45, Walter Bright wrote:
>>> On 6/13/2019 12:51 PM, Timon Gehr wrote:
>>>> On 13.06.19 21:44, Walter Bright wrote:
>>>>> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>>>>>> Yes. IMHO this shouldn't be a @safe pure operation.
>>>>>
>>>>> D regards converting a pointer to an int as safe, but not the other way around.
>>>>
>>>> I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.
>>> 
>>> We had a discussion a while back about whether allocating memory via new was pure or not, since a different value was produced each time. We eventually took the pragmatic route that pure functions would be next to useless if this was not allowed.
>>
>> 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.
>
> And you can't return the pointer to something in those languages. It is entirely dependent on the language, this is not a functional language. You can return pointers. What do you do when you expect two calls to have different allocated objects that have the same value? Does the compiler simply produce one object, or two or more?

*pure functional language
June 14, 2019
On 14.06.19 02:28, Exil wrote:
> On Thursday, 13 June 2019 at 21:08:42 UTC, Timon Gehr wrote:
>> On 13.06.19 22:20, Exil wrote:
>>> On Thursday, 13 June 2019 at 19:51:12 UTC, Timon Gehr wrote:
>>>> On 13.06.19 21:44, Walter Bright wrote:
>>>>> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>>>>>> Yes. IMHO this shouldn't be a @safe pure operation.
>>>>>
>>>>> D regards converting a pointer to an int as safe, but not the other way around.
>>>>
>>>> I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.
>>>
>>> The operation is pure.
>>
>> No.
> 
> What global state is it accessing then? If you remove the new allocate and just use a passed in pointer parameter.
> ...

It's accessing the address, which is dependent on global state. The caller is calling a pure function, so the address of the integer is not conceptually part of the information that is passed to it, it is external state. It's not what the caller signs up for when calling a pure function. It messes with the guarantees that pure is supposed to provide; they should be similar to what you get in pure functional programming languages.

>>> Accessing global state is the part that is not pure. For convience I guess you can allocate memory, ...
>>
>> It's an _abstraction_. You allocate because you need memory to represent your values, not because you want to know at which address your values will be located. The former is a necessity, the latter is not pure.
>>
>> Purely functional programming languages also allocate memory at runtime, but it is hidden in the runtime system and kept separate from the language definition. In D, user code and runtime system can both be in the same code base, but the runtime system parts need to be in impure or pure @system code, not in your pure code.
> 
> Yes, that is not pure. It's a compromise for usability.
> ...

Casting pointers to integers in pure @safe code is not a compromise for usability, it's simply an oversight that needs to be corrected. If you have a good reason to do it, write some pure @trusted code.

>>> ... > Pure for the most part guarantees you don't
>>> access global variables.
>>
>> No, it does not. It guarantees you don't read or write implicit state (i.e. state not accessible through the function arguments).
> 
> Tomato tomato.
> ...

No. Immutable variables are variables. Accessing a global via a ref argument is an access. If you don't care about being precise, please stop wasting my time.

>>> Pure doesn't mean deterministic.
>>
>> Yes, it does. A function can only fail to be deterministic if it accesses implicit state that changes between function invocations.
> 
> Which gets violated anyways because of the GC.
> 
> 

Not if you can't access memory addresses. Do you understand abstraction? Do you have functional programming experience?
June 14, 2019
On 14.06.19 02:49, Exil wrote:
> On Friday, 14 June 2019 at 00:35:10 UTC, Timon Gehr wrote:
>> On 13.06.19 23:45, Walter Bright wrote:
>>> On 6/13/2019 12:51 PM, Timon Gehr wrote:
>>>> On 13.06.19 21:44, Walter Bright wrote:
>>>>> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>>>>>> Yes. IMHO this shouldn't be a @safe pure operation.
>>>>>
>>>>> D regards converting a pointer to an int as safe, but not the other way around.
>>>>
>>>> I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.
>>>
>>> We had a discussion a while back about whether allocating memory via new was pure or not, since a different value was produced each time. We eventually took the pragmatic route that pure functions would be next to useless if this was not allowed.
>>
>> 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.
> 
> And you can't return the pointer to something in those languages.

That's bullshit. http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-STRef.html

And anyway, you _need_ pointers/arrays/class references to represent most non-trivial data structures in D.

> It is entirely dependent on the language, this is not a functional language. 

The point of `pure` is to get the same benefits, selectively, even if you don't care for introductory category theory and want to keep C-style control.

> You can return pointers. What do you do when you expect two calls to have different allocated objects that have the same value? Does the compiler simply produce one object, or two or more?
> 
> 

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.)

June 14, 2019
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?

> 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.
June 14, 2019
On Friday, 14 June 2019 at 00:56:11 UTC, Timon Gehr wrote:
> On 14.06.19 02:28, Exil wrote:
>> On Thursday, 13 June 2019 at 21:08:42 UTC, Timon Gehr wrote:
>>> On 13.06.19 22:20, Exil wrote:
>>>> On Thursday, 13 June 2019 at 19:51:12 UTC, Timon Gehr wrote:
>>>>> On 13.06.19 21:44, Walter Bright wrote:
>>>>>> On 6/13/2019 4:30 AM, Timon Gehr wrote:
>>>>>>> Yes. IMHO this shouldn't be a @safe pure operation.
>>>>>>
>>>>>> D regards converting a pointer to an int as safe, but not the other way around.
>>>>>
>>>>> I know, and that is fine, but casting a pointer to int is not pure. It glances into the global state maintained by the memory allocator.
>>>>
>>>> The operation is pure.
>>>
>>> No.
>> 
>> What global state is it accessing then? If you remove the new allocate and just use a passed in pointer parameter.
>> ...
>
> It's accessing the address, which is dependent on global state. The caller is calling a pure function, so the address of the integer is not conceptually part of the information that is passed to it, it is external state. It's not what the caller signs up for when calling a pure function. It messes with the guarantees that pure is supposed to provide; they should be similar to what you get in pure functional programming languages.
>
>>>> Accessing global state is the part that is not pure. For convience I guess you can allocate memory, ...
>>>
>>> It's an _abstraction_. You allocate because you need memory to represent your values, not because you want to know at which address your values will be located. The former is a necessity, the latter is not pure.
>>>
>>> Purely functional programming languages also allocate memory at runtime, but it is hidden in the runtime system and kept separate from the language definition. In D, user code and runtime system can both be in the same code base, but the runtime system parts need to be in impure or pure @system code, not in your pure code.
>> 
>> Yes, that is not pure. It's a compromise for usability.
>> ...
>
> Casting pointers to integers in pure @safe code is not a compromise for usability, it's simply an oversight that needs to be corrected. If you have a good reason to do it, write some pure @trusted code.

Why would you have this feature, that only relates to "pure" only work in @safe? I don't think @safe combines with other attributes to make them mean something different, nor should it.

>>>> ... > Pure for the most part guarantees you don't
>>>> access global variables.
>>>
>>> No, it does not. It guarantees you don't read or write implicit state (i.e. state not accessible through the function arguments).
>> 
>> Tomato tomato.
>> ...
>
> No. Immutable variables are variables. Accessing a global via a ref argument is an access. If you don't care about being precise, please stop wasting my time.
>
>>>> Pure doesn't mean deterministic.
>>>
>>> Yes, it does. A function can only fail to be deterministic if it accesses implicit state that changes between function invocations.
>> 
>> Which gets violated anyways because of the GC.
>> 
>> 
>
> Not if you can't access memory addresses. Do you understand abstraction? Do you have functional programming experience?

This is not a pure functional programming language though. You can access pointers, even if the pure function doesn't, it's call site could.

June 14, 2019
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.

>> 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.