July 02, 2017
On 07/01/2017 07:55 PM, Timon Gehr wrote:
> On 02.07.2017 01:08, Andrei Alexandrescu wrote:
>> Vaguely related question: should "const" convert implicitly to "const shared"? The intuition is that the latter offers even less guarantees than the former so it's the more general type. See http://erdani.com/conversions3.svg.
>>
>> That would be nice because we have "const shared" as the unique root of the qualifier hierarchy.
> 
> This means that there can be aliasing between an unqualified reference and a const shared reference. Therefore, you can have code that mutates unshared data while another thread is reading it.
> 
> What should the semantics of this be?
> 
> The only potential issue is that it could restrict code operating on unshared data because it needs to play nice in some way to allow consistent data to be read by another thread.

Well const shared exists already with the semantics of "you can't modify this and you must load it atomically to look at it". The question is whether the conversion from const to const shared can be allowed. -- Andrei
July 02, 2017
On 02/07/17 15:31, Andrei Alexandrescu wrote:
> That supports the case for allowing the conversion.
> 
> const: "You have a view to data that this thread may or may not change."
> 
> const shared: "You have a view to data that any thread may or may not change."
> 
> So the set of const is included in the set of const shared - texbook inclusion polymorphism.
> 

It does, with two (or is that three?) caveats.

First, I have 0 (zero) experience with shared, so I don't know what barriers are used on access. If they're expensive, this combining of the type system might be a problem.

Second, there are optimizations that can take place over "const" that cannot over "shared const" even assuming aliasing (such as if the compiler knows no other pointer was changed between two accesses).

The last point is that assuming no pointer aliasing is a fairly common optimization to take in C and C++, simply because of the huge performance gains it provides. It is so huge that it is sometimes turned on by default despite the fact it changes language semantics. It would be a pity to block any potential to have it in D.

Just my humble opinion.

Shachar
July 02, 2017
On 07/02/2017 08:46 AM, Shachar Shemesh wrote:
> Second, there are optimizations that can take place over "const" that cannot over "shared const" even assuming aliasing (such as if the compiler knows no other pointer was changed between two accesses).

Wouldn't that also fall within the realm of inclusion polymorphism?

> The last point is that assuming no pointer aliasing is a fairly common optimization to take in C and C++, simply because of the huge performance gains it provides. It is so huge that it is sometimes turned on by default despite the fact it changes language semantics. It would be a pity to block any potential to have it in D.

Allowing the conversion does not preclude any optimization; after all there is no replacement of one with another. The conversion simply removes unnecessary restrictions.


Andrei
July 02, 2017
On 02.07.2017 14:41, Andrei Alexandrescu wrote:
> On 07/01/2017 07:55 PM, Timon Gehr wrote:
>> On 02.07.2017 01:08, Andrei Alexandrescu wrote:
>>> Vaguely related question: should "const" convert implicitly to "const shared"? The intuition is that the latter offers even less guarantees than the former so it's the more general type. See http://erdani.com/conversions3.svg.
>>>
>>> That would be nice because we have "const shared" as the unique root of the qualifier hierarchy.
>>
>> This means that there can be aliasing between an unqualified reference and a const shared reference. Therefore, you can have code that mutates unshared data while another thread is reading it.
>>
>> What should the semantics of this be?
>>
>> The only potential issue is that it could restrict code operating on unshared data because it needs to play nice in some way to allow consistent data to be read by another thread.
> 
> Well const shared exists already with the semantics of "you can't modify this and you must load it atomically to look at it". The question is whether the conversion from const to const shared can be allowed. -- Andrei

If the data is not written atomically, how can it be loaded atomically?
July 02, 2017
On 07/02/2017 09:07 AM, Timon Gehr wrote:
> On 02.07.2017 14:41, Andrei Alexandrescu wrote:
>> On 07/01/2017 07:55 PM, Timon Gehr wrote:
>>> On 02.07.2017 01:08, Andrei Alexandrescu wrote:
>>>> Vaguely related question: should "const" convert implicitly to "const shared"? The intuition is that the latter offers even less guarantees than the former so it's the more general type. See http://erdani.com/conversions3.svg.
>>>>
>>>> That would be nice because we have "const shared" as the unique root of the qualifier hierarchy.
>>>
>>> This means that there can be aliasing between an unqualified reference and a const shared reference. Therefore, you can have code that mutates unshared data while another thread is reading it.
>>>
>>> What should the semantics of this be?
>>>
>>> The only potential issue is that it could restrict code operating on unshared data because it needs to play nice in some way to allow consistent data to be read by another thread.
>>
>> Well const shared exists already with the semantics of "you can't modify this and you must load it atomically to look at it". The question is whether the conversion from const to const shared can be allowed. -- Andrei
> 
> If the data is not written atomically, how can it be loaded atomically?

Then you atomically load parts of it, the point being that the matter is present in the type. I must not be understanding the question. -- Andrei
July 02, 2017
On 02.07.2017 10:55, Walter Bright wrote:
> Neither I nor anyone here seems to understand its purpose.

The opposite is true. I understand it, and you seem to understand it partially:

On 02.07.2017 05:55, Walter Bright wrote:
> On 7/1/2017 6:22 PM, Stefan Koch wrote:
>> I cannot think so a single time I ever used const inout.
> 
> The math needs to work whether it is ever used or not, otherwise we wind up with bizarre, intractable absurdities.

> 
> It's my fault as I should have noticed this getting slipped into the compiler. 

No, it is Kenji Hara's and my achievement. This is one of the things Kenji slipped into the language that actually should be there. If you have 'inout' there is no way around 'const inout'.

> The existence of it is likely a significant contributor to peoples' confusion about inout.

The opposite is true. Many people are confused about inout and by extension about const inout.

The best way to think about inout is that it enables a function to have three distinct signatures:

inout(int)[] foo(inout(int)[] arg);

"expands" to:

int[] foo(int[] arg);
immutable(int)[] foo(immutable(int)[] arg);
const(int)[] foo(const(int)[] arg);


const inout /does not change this in any way/:


const(inout(int))[] foo(inout(int)[] arg);

expands to:

const(int)[] foo(int[] arg);
const(immutable(int))[] foo(immutable(int)[] arg);
const(const(int))[] foo(const(int)[] arg);

It would be confusing if it worked any differently.
July 02, 2017
On 02.07.2017 15:29, Andrei Alexandrescu wrote:
> On 07/02/2017 09:07 AM, Timon Gehr wrote:
>> On 02.07.2017 14:41, Andrei Alexandrescu wrote:
>>> On 07/01/2017 07:55 PM, Timon Gehr wrote:
>>>> On 02.07.2017 01:08, Andrei Alexandrescu wrote:
>>>>> Vaguely related question: should "const" convert implicitly to "const shared"? The intuition is that the latter offers even less guarantees than the former so it's the more general type. See http://erdani.com/conversions3.svg.
>>>>>
>>>>> That would be nice because we have "const shared" as the unique root of the qualifier hierarchy.
>>>>
>>>> This means that there can be aliasing between an unqualified reference and a const shared reference. Therefore, you can have code that mutates unshared data while another thread is reading it.
>>>>
>>>> What should the semantics of this be?
>>>>
>>>> The only potential issue is that it could restrict code operating on unshared data because it needs to play nice in some way to allow consistent data to be read by another thread.
>>>
>>> Well const shared exists already with the semantics of "you can't modify this and you must load it atomically to look at it". The question is whether the conversion from const to const shared can be allowed. -- Andrei
>>
>> If the data is not written atomically, how can it be loaded atomically?
> 
> Then you atomically load parts of it, the point being that the matter is present in the type. I must not be understanding the question. -- Andrei

In general, depending on the hardware memory model and the language memory model, data transfer from one thread to another requires cooperation from both parties. We don't want the thread that has the unshared data to need to participate in such a cooperation.
July 02, 2017
On 02.07.2017 14:31, Andrei Alexandrescu wrote:
> On 07/02/2017 02:49 AM, Shachar Shemesh wrote:
>> On 02/07/17 02:08, Andrei Alexandrescu wrote:
>>> Vaguely related question: should "const" convert implicitly to "const shared"? The intuition is that the latter offers even less guarantees than the former so it's the more general type. See http://erdani.com/conversions3.svg.
>>
>> I don't see how it can. They provide different guarantees. If anything, it should be the other way around.
>>
>> If you hold a pointer to const, you know the data will not change during the function's execution. No such guarantees for const shared.
> 
> That supports the case for allowing the conversion.
> 
> const: "You have a view to data that this thread may or may not change."
> 
> const shared: "You have a view to data that any thread may or may not change."
> 
> So the set of const is included in the set of const shared - texbook inclusion polymorphism.

This is not the whole story. The best way to think about 'shared' is that it enables guarantees about data that does /not/ have the qualifier. So we need to look at what this change does to the meaning of data being unqualified:

It changes from:

unqualified: This data can be read and written exclusively by the current thread.

to

unqualified: This data can be read and written by this thread and read by any other thread.


This disallows some program transformations that were allowed before unless reading from a const shared reference while a mutable thread is writing to it has an undefined result, in which case the change just removes /all/ guarantees from const shared just in order to place it at the top of the hierarchy. At this point you can just use void*.
July 02, 2017
On 07/02/2017 09:39 AM, Timon Gehr wrote:
> In general, depending on the hardware memory model and the language memory model, data transfer from one thread to another requires cooperation from both parties. We don't want the thread that has the unshared data to need to participate in such a cooperation.

Yes, there must be a handshake. Oh, I see your point. Let me illustrate:

void fun(const shared int* p1)
{
   auto a = atomicLoad(p1);
   ...
}

void gun()
{
    int* p = new int;
    shared const int* p1 = p; // assume this passes
    spawn(&fun, p);
    *p = 42; // should be a shared write, it's not
}

Is this what you're referring to?

So it seems like the hierarchy in http://erdani.com/conversions.svg is minimal?


Andrei
July 02, 2017
On 07/02/2017 09:48 AM, Andrei Alexandrescu wrote:
>      *p = 42; // should be a shared write, it's not

I meant:

    p = new int; // should be a shared write, it's not


Andrei