www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why do "const inout" and "const inout shared" exist?

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter looked at http://erdani.com/conversions.svg and said actually 
"const inout" and "const inout shared" should not exist as distinct 
qualifier groups, leading to the simplified qualifier hierarcy in 
http://erdani.com/conversions-simplified.svg.

Are we missing something? Is there a need for combining const and inout?


Thanks,

Andrei
Jul 01
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 1 July 2017 at 21:47:20 UTC, Andrei Alexandrescu 
wrote:
 Walter looked at http://erdani.com/conversions.svg and said 
 actually "const inout" and "const inout shared" should not 
 exist as distinct qualifier groups, leading to the simplified 
 qualifier hierarcy in 
 http://erdani.com/conversions-simplified.svg.

 Are we missing something? Is there a need for combining const 
 and inout?


 Thanks,

 Andrei
inout is bascially the same as const for all parctical purposes.
Jul 01
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 00:10, Stefan Koch wrote:
 On Saturday, 1 July 2017 at 21:47:20 UTC, Andrei Alexandrescu wrote:
 Walter looked at http://erdani.com/conversions.svg and said actually 
 "const inout" and "const inout shared" should not exist as distinct 
 qualifier groups, leading to the simplified qualifier hierarcy in 
 http://erdani.com/conversions-simplified.svg.

 Are we missing something? Is there a need for combining const and inout?


 Thanks,

 Andrei
inout is bascially the same as const for all parctical purposes.
struct S{ int x; ref inout(int) foo()inout{ return x; } } void main(){ S s; s.foo()++; // ok! const(S) t = s; import std.stdio; writeln(t.foo()); // t.foo()++; // error }
Jul 01
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 1 July 2017 at 22:16:12 UTC, Timon Gehr wrote:
 struct S{
     int x;
     ref inout(int) foo()inout{
         return x;
     }
 }

 void main(){
     S s;
     s.foo()++; // ok!
     const(S) t = s;
     import std.stdio;
     writeln(t.foo());
     // t.foo()++; // error
 }
Oh damn. I was not aware that it could behave non-constly. since when does it do that ?
Jul 01
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 00:26, Stefan Koch wrote:
 On Saturday, 1 July 2017 at 22:16:12 UTC, Timon Gehr wrote:
 struct S{
     int x;
     ref inout(int) foo()inout{
         return x;
     }
 }

 void main(){
     S s;
     s.foo()++; // ok!
     const(S) t = s;
     import std.stdio;
     writeln(t.foo());
     // t.foo()++; // error
 }
Oh damn. I was not aware that it could behave non-constly. since when does it do that ?
Since the beginning. :) The point of inout is in essence to allow writing an identity function that can operate on data of any mutability qualifier with support for virtual calls and without duplicating code in the binary.
Jul 01
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01.07.2017 23:47, Andrei Alexandrescu wrote:
 Walter looked at http://erdani.com/conversions.svg and said actually 
 "const inout" and "const inout shared" should not exist as distinct 
 qualifier groups, leading to the simplified qualifier hierarcy in 
 http://erdani.com/conversions-simplified.svg.
 
 Are we missing something?
I don't think so.
 Is there a need for combining const and inout?
 ...
In DMD's implementation, yes. (Combinations of qualifiers are represented as integers instead of nested AST nodes.) const(const(T)) = const(T) const(immutable(T)) = immutable(T) const(inout(T)) = ? It used to be the case that const(inout(T)) = const(T), but this is wrong, because if we replace 'inout' by 'immutable', the result should be immutable(T), not const(T). Hence const(inout(T)) cannot be reduced further. The simplified hierarchy is enough though. The more complex one can be derived from it. Since S -> const(S) for all S, it directly follows that inout(T) -> const(inout(T)) for all T (we can choose S=inout(T)).
Jul 01
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/01/2017 06:12 PM, Timon Gehr wrote:
 On 01.07.2017 23:47, Andrei Alexandrescu wrote:
 Walter looked at http://erdani.com/conversions.svg and said actually 
 "const inout" and "const inout shared" should not exist as distinct 
 qualifier groups, leading to the simplified qualifier hierarcy in 
 http://erdani.com/conversions-simplified.svg.

 Are we missing something?
I don't think so.
 Is there a need for combining const and inout?
 ...
In DMD's implementation, yes. (Combinations of qualifiers are represented as integers instead of nested AST nodes.) const(const(T)) = const(T) const(immutable(T)) = immutable(T) const(inout(T)) = ? It used to be the case that const(inout(T)) = const(T), but this is wrong, because if we replace 'inout' by 'immutable', the result should be immutable(T), not const(T). Hence const(inout(T)) cannot be reduced further. The simplified hierarchy is enough though. The more complex one can be derived from it. Since S -> const(S) for all S, it directly follows that inout(T) -> const(inout(T)) for all T (we can choose S=inout(T)).
Thanks! Well I do want to have a hierarchy with all possible qualifier combinations for utmost clarity. Only the combinations listed are valid, e.g. there's no "immutable inout" or whatever. 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. Andrei
Jul 01
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 01:08, Andrei Alexandrescu wrote:
 
 Thanks! Well I do want to have a hierarchy with all possible qualifier 
 combinations for utmost clarity. Only the combinations listed are valid, 
 e.g. there's no "immutable inout" or whatever.
 ...
immutable(inout(T)) is valid syntax, but this type is equal to immutable(T).
 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.
Jul 01
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Jul 02
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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?
Jul 02
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Jul 02
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Jul 02
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Jul 02
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Jul 02
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/02/2017 09:49 AM, Andrei Alexandrescu wrote:
 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
Dognabbit. No, I meant the previous one! The pointer itself is private to gun. -- Andrei
Jul 02
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 15:48, Andrei Alexandrescu wrote:
 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? ...
Yes, precisely.
 So it seems like the hierarchy in http://erdani.com/conversions.svg is 
 minimal?
 
Yes, I think there is no way to collapse it without bad consequences.
Jul 02
prev sibling next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 1 July 2017 at 23:08:40 UTC, Andrei Alexandrescu 
wrote:
 On 07/01/2017 06:12 PM, Timon Gehr wrote:
 On 01.07.2017 23:47, Andrei Alexandrescu wrote:
 Walter looked at http://erdani.com/conversions.svg and said 
 actually "const inout" and "const inout shared" should not 
 exist as distinct qualifier groups, leading to the simplified 
 qualifier hierarcy in 
 http://erdani.com/conversions-simplified.svg.

 Are we missing something?
I don't think so.
 Is there a need for combining const and inout?
 ...
In DMD's implementation, yes. (Combinations of qualifiers are represented as integers instead of nested AST nodes.) const(const(T)) = const(T) const(immutable(T)) = immutable(T) const(inout(T)) = ? It used to be the case that const(inout(T)) = const(T), but this is wrong, because if we replace 'inout' by 'immutable', the result should be immutable(T), not const(T). Hence const(inout(T)) cannot be reduced further. The simplified hierarchy is enough though. The more complex one can be derived from it. Since S -> const(S) for all S, it directly follows that inout(T) -> const(inout(T)) for all T (we can choose S=inout(T)).
Thanks! Well I do want to have a hierarchy with all possible qualifier combinations for utmost clarity. Only the combinations listed are valid, e.g. there's no "immutable inout" or whatever. 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. Andrei
I cannot think so a single time I ever used const inout.
Jul 01
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 03:22, Stefan Koch wrote:
 ...
 
 I cannot think so a single time I ever used const inout.
I'll boldly claim that this is true mostly because you have never used 'inout'. ;)
Jul 01
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 2 July 2017 at 01:51:05 UTC, Timon Gehr wrote:
 On 02.07.2017 03:22, Stefan Koch wrote:
 ...
 
 I cannot think so a single time I ever used const inout.
I'll boldly claim that this is true mostly because you have never used 'inout'. ;)
affirmative
Jul 01
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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.
Jul 01
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 2 July 2017 at 03:55:37 UTC, 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.
I agree. I may add though that we already have a few absurdities ... inout itself for example :)
Jul 01
prev sibling parent reply Shachar Shemesh <shachar weka.io> writes:
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. On second thought, aliasing means that the first is not true either. I retract the above comment, sending it out on the off-chance someone can turn it into a useful insight :-) Shachar
Jul 01
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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. Andrei
Jul 02
next sibling parent reply Shachar Shemesh <shachar weka.io> writes:
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
Jul 02
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Jul 02
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/2/2017 5: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).
 
 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.
Pointer aliasing is indeed a big deal for optimization. But that really has nothing to do with const in C++. The trouble with C++ const is you can legally cast it away in C++. Chandler Carruth (LLVM optimizer) told me that const was basically ignored by the optimizer because of that. D const is different in that the compiler is allowed to generate code as if const was never cast to immutable. Such casts are also not allowed in safe code.
Jul 02
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 3 July 2017 at 01:15:47 UTC, Walter Bright wrote:
 D const is different in that the compiler is allowed to 
 generate code as if const was never cast to immutable. Such 
 casts are also not allowed in  safe code.
You probably meant mutable, but how does that bring any advantages if any other thread can mutate it? Seems like you would want something closer to Pony's or Rust's type system to gain any real benefits in terms of optimization.
Jul 03
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
I also think it would be a good idea for the D crowd to be 
specific when they compare to C++ const, which should be compared 
to D's shared version of const and not to the local variety.

C/C++ compilers provide other means to provide the optimizer with 
information about actual mutation/aliasing than const-types. So 
it doesn't really compare well.

Besides, none of this makes a lot of sense until you have fully 
specified a sound memory model + type system for shared...
Jul 03
prev sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Monday, 3 July 2017 at 07:22:08 UTC, Ola Fosheim Grøstad wrote:
 On Monday, 3 July 2017 at 01:15:47 UTC, Walter Bright wrote:
 D const is different in that the compiler is allowed to 
 generate code as if const was never cast to immutable. Such 
 casts are also not allowed in  safe code.
You probably meant mutable, but how does that bring any advantages if any other thread can mutate it?
Unlike C++, in D objects can't be shared across threads, unless they are marked as `shared` (modulo un-` safe` code - like casts and `__gshared` - and compiler bugs). I.e. non-`shared` objects can't be mutated by more than one thread. Combined with `pure` functions, the guarantees provided by D's type system are quite useful: ``` void main() { int x = globalArray.foo(); } // module-level non-shared variables are thread-local int[] globalArray = [1, 2, 3, 4]; int foo(const(int)[] array) pure { // globalArray[0] = 42; doesn't compile // For all intents and purposes, the elements of `array` can // be viewed as immutable here and they are *not* aliased. // ... } ```
 Seems like you would want something closer to Pony's or Rust's 
 type system to gain any real benefits in terms of optimization.
I've watched a presentation on Pony's type system and it is indeed interesting, but can you explain exactly what part of Rust's type system provides extra benefits in terms of optimization over D's type system?
Jul 03
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 3 July 2017 at 15:48:26 UTC, Petar Kirov [ZombineDev] 
wrote:
 Unlike C++, in D objects can't be shared across threads, unless 
 they are marked as `shared` (modulo un-` safe` code - like
I understand the intent, but since everything is "shared" in C++, unless you annotate variables with optimization-constraints, it doesn't make sense to compare C++ const to D const. One should either compare C++ const do D shared const, or compare C++ const-with-constraints with D const.
 int foo(const(int)[] array) pure
 {
     // globalArray[0] = 42; doesn't compile
     // For all intents and purposes, the elements of `array` can
     // be viewed as immutable here and they are *not* aliased.
They aren't aliased because you only have one parameter with non-reference values. That's a rather narrow use-case…
 I've watched a presentation on Pony's type system and it is 
 indeed interesting,
Yes, e.g. Pony have "isolated" (e.g. no aliasing) with transitions to less constrained types.
 but can you explain exactly what part of Rust's type system 
 provides extra
 benefits in terms of optimization over D's type system?
As I understand it Rust's static analysis is designed to track aliasing using linear/affine typing for objects.
Jul 03
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/3/2017 8:48 AM, Petar Kirov [ZombineDev] wrote:
 Unlike C++, in D objects can't be shared across threads, unless they are
marked 
 as `shared` (modulo un-` safe` code - like casts and `__gshared` - and
compiler 
 bugs).
 I.e. non-`shared` objects can't be mutated by more than one thread.
 Combined with `pure` functions, the guarantees provided by D's type system are 
 quite useful:
 
 ```
 void main()
 {
      int x = globalArray.foo();
 }
 
 // module-level non-shared variables are thread-local
 int[] globalArray = [1, 2, 3, 4];
 
 int foo(const(int)[] array) pure
 {
      // globalArray[0] = 42; doesn't compile
      // For all intents and purposes, the elements of `array` can
      // be viewed as immutable here and they are *not* aliased.
 
      // ...
 }
Keep in mind that today's optimizers are pretty much tuned to what works for C++. While D's particular semantics offer opportunities for optimizations, they are currently pretty much unexploited.
Jul 03
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Mon, Jul 03, 2017 at 11:49:56AM -0700, Walter Bright via Digitalmars-d wrote:
[...]
 Keep in mind that today's optimizers are pretty much tuned to what
 works for C++. While D's particular semantics offer opportunities for
 optimizations, they are currently pretty much unexploited.
So what stops us from actually exploiting them in dmd's optimizer? T -- Knowledge is that area of ignorance that we arrange and classify. -- Ambrose Bierce
Jul 03
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/3/2017 5:29 PM, H. S. Teoh via Digitalmars-d wrote:
 So what stops us from actually exploiting them in dmd's optimizer?
Nothing. Pull requests are welcome!
Jul 04
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Jul 04, 2017 at 12:30:20AM -0700, Walter Bright via Digitalmars-d wrote:
 On 7/3/2017 5:29 PM, H. S. Teoh via Digitalmars-d wrote:
 So what stops us from actually exploiting them in dmd's optimizer?
Nothing. Pull requests are welcome!
Sure, but it would be helpful if we knew what are some of the things about D semantics that the optimizer could take advantage of, that it currently doesn't yet. Do you have a (not necessarily complete) list somewhere? T -- Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. -- Brian W. Kernighan
Jul 05
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Monday, 3 July 2017 at 15:48:26 UTC, Petar Kirov [ZombineDev] 
wrote:
 but can you explain exactly what part of Rust's type system 
 provides extra
 benefits in terms of optimization over D's type system?
In safe Rust, a reference to mutable data has to be unique [1]. So the optimizer could assume no aliasing as long as the mutable borrow lasts. [1] https://rustbyexample.com/scope/borrow/alias.html
Jul 03
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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*.
Jul 02
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/1/2017 3:12 PM, Timon Gehr wrote:
 const(const(T))     = const(T)
 const(immutable(T)) = immutable(T)
 const(inout(T))     = ?
 
 It used to be the case that const(inout(T)) = const(T), but this is wrong, 
 because if we replace 'inout' by 'immutable', the result should be
immutable(T), 
 not const(T). Hence const(inout(T)) cannot be reduced further.
If const(inout(T)) is reduced to inout(T), it works.
Jul 01
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 05:13, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 const(const(T))     = const(T)
 const(immutable(T)) = immutable(T)
 const(inout(T))     = ?

 It used to be the case that const(inout(T)) = const(T), but this is 
 wrong, because if we replace 'inout' by 'immutable', the result should 
 be immutable(T), not const(T). Hence const(inout(T)) cannot be reduced 
 further.
If const(inout(T)) is reduced to inout(T), it works.
Counterexample: const(inout(char))[] foo(bool condition, inout(char)[] chars){ if(!condition) return "condition failed!"; return chars; } Turn const(inout(char)) into inout(char) and the example no longer compiles. (Nor should it.)
Jul 01
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/1/2017 9:12 PM, Timon Gehr wrote:
 On 02.07.2017 05:13, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 const(const(T))     = const(T)
 const(immutable(T)) = immutable(T)
 const(inout(T))     = ?

 It used to be the case that const(inout(T)) = const(T), but this is wrong, 
 because if we replace 'inout' by 'immutable', the result should be 
 immutable(T), not const(T). Hence const(inout(T)) cannot be reduced further.
If const(inout(T)) is reduced to inout(T), it works.
Counterexample: const(inout(char))[] foo(bool condition, inout(char)[] chars){ if(!condition) return "condition failed!"; return chars; } Turn const(inout(char)) into inout(char) and the example no longer compiles. (Nor should it.)
I don't think that matters. There's no reason to write const(inout) for a return value.
Jul 01
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 06:45, Walter Bright wrote:
 On 7/1/2017 9:12 PM, Timon Gehr wrote:
 On 02.07.2017 05:13, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 const(const(T))     = const(T)
 const(immutable(T)) = immutable(T)
 const(inout(T))     = ?

 It used to be the case that const(inout(T)) = const(T), but this is 
 wrong, because if we replace 'inout' by 'immutable', the result 
 should be immutable(T), not const(T). Hence const(inout(T)) cannot 
 be reduced further.
If const(inout(T)) is reduced to inout(T), it works.
Counterexample: const(inout(char))[] foo(bool condition, inout(char)[] chars){ if(!condition) return "condition failed!"; return chars; } Turn const(inout(char)) into inout(char) and the example no longer compiles. (Nor should it.)
I don't think that matters. There's no reason to write const(inout) for a return value.
I think the example demonstrate the reason. It either returns the argument or an immutable global. If the argument is immutable, so is the return value, otherwise the return value is const.
Jul 01
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/1/2017 9:49 PM, Timon Gehr wrote:
 On 02.07.2017 06:45, Walter Bright wrote:
 On 7/1/2017 9:12 PM, Timon Gehr wrote:
 On 02.07.2017 05:13, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 const(const(T))     = const(T)
 const(immutable(T)) = immutable(T)
 const(inout(T))     = ?

 It used to be the case that const(inout(T)) = const(T), but this is wrong, 
 because if we replace 'inout' by 'immutable', the result should be 
 immutable(T), not const(T). Hence const(inout(T)) cannot be reduced further.
If const(inout(T)) is reduced to inout(T), it works.
Counterexample: const(inout(char))[] foo(bool condition, inout(char)[] chars){ if(!condition) return "condition failed!"; return chars; } Turn const(inout(char)) into inout(char) and the example no longer compiles. (Nor should it.)
I don't think that matters. There's no reason to write const(inout) for a return value.
I think the example demonstrate the reason. It either returns the argument or an immutable global. If the argument is immutable, so is the return value, otherwise the return value is const.
The purpose of inout is to transmit the mutable/const/immutable attribute of the argument to the return type. If you want the return type to be const, mark it const, not const inout. I can't think of any useful purpose to const inout. The foo() example is bogus anyway. The return type inout calculus has nothing to do with the return expressions. It's not going to be immutable if the `return "condition failed";` is executed. It only depends on the attribute of the argument to the `chars` parameter.
Jul 01
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Sun, Jul 02, 2017 at 06:49:39AM +0200, Timon Gehr via Digitalmars-d wrote:
 On 02.07.2017 06:45, Walter Bright wrote:
 On 7/1/2017 9:12 PM, Timon Gehr wrote:
 On 02.07.2017 05:13, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 const(const(T))     = const(T)
 const(immutable(T)) = immutable(T)
 const(inout(T))     = ?
 
 It used to be the case that const(inout(T)) = const(T), but
 this is wrong, because if we replace 'inout' by 'immutable',
 the result should be immutable(T), not const(T). Hence
 const(inout(T)) cannot be reduced further.
If const(inout(T)) is reduced to inout(T), it works.
Counterexample: const(inout(char))[] foo(bool condition, inout(char)[] chars){ if(!condition) return "condition failed!"; return chars; } Turn const(inout(char)) into inout(char) and the example no longer compiles. (Nor should it.)
I don't think that matters. There's no reason to write const(inout) for a return value.
I think the example demonstrate the reason. It either returns the argument or an immutable global. If the argument is immutable, so is the return value, otherwise the return value is const.
Whoa, wait a second here. Isn't this completely over-engineering something that's actually quite simple?! The idea of inout is that a function's parameter is essentially const, and the function body treats it like const, and returns it as-is. Therefore, it is it safe for the *caller* to assume that if the argument is mutable, then the function's return value is also mutable. This last part is essentially the only reason inout exists; otherwise we would just write it as const. If the function may possibly return something other than the inout argument, then it is actually wrong to annotate the parameter as inout (e.g., pass in a mutable object, get an immutable back which the caller thinks is mutable). In such a case, the correct annotation is const, not inout. In your example, inout makes no sense at all. It should be written as: const(char)[] foo(bool condition, const(char)[] chars) because if it returns the argument, then it's const(char)[], and if it returns an immutable global, then immutable(char)[] implicitly converts to const(char)[]. Using inout doesn't make sense here because you cannot assume that the return value is mutable if the parameter is mutable -- the function may return immutable instead. T -- "Holy war is an oxymoron." -- Lazarus Long
Jul 01
next sibling parent reply =?UTF-8?Q?S=c3=b6nke_Ludwig?= <sludwig outerproduct.org> writes:
Am 02.07.2017 um 08:39 schrieb H. S. Teoh via Digitalmars-d:
 In your example, inout makes no sense at all. It should be written as:
 
 	const(char)[] foo(bool condition, const(char)[] chars)
 
 because if it returns the argument, then it's const(char)[], and if it
 returns an immutable global, then immutable(char)[] implicitly converts
 to const(char)[].  Using inout doesn't make sense here because you
 cannot assume that the return value is mutable if the parameter is
 mutable -- the function may return immutable instead.
 
 
 T
 
The idea is to be able to write: string result = foo(true, "bar"); There are arguments for both sides. On one hand, inout is already a very specific hack in the language that often isn't applicable when it would be handy, and a restriction here would probably not change much. On the other hand it's always desirable to generalize a language feature as much as possible, to get the maximum out of the complexity investment. However, in this case it can be also argued that supporting this specific case adds it's own complexity. The concept of const(inout(T)) definitely sounds like one of the harder ones to explain. So I'd probably still opt for simplifying the conversion hierarchy.
Jul 02
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/2/2017 1:12 AM, Sönke Ludwig wrote:
 The idea is to be able to write: string result = foo(true, "bar");
If it is declared as: inout(char)[] foo(bool condition, inout(char)[] chars); your specific case will work as expected. Perhaps you meant: char[] s; string result = foo(true, s); which will never work, even if const inout is a type, because the returns in the function body do NOT determine the return type's inout meaning. Only the type of the argument supplied to the inout parameter does that.
 However, in this case it can be also argued that supporting this specific case 
 adds it's own complexity. The concept of const(inout(T)) definitely sounds
like 
 one of the harder ones to explain. So I'd probably still opt for simplifying
the 
 conversion hierarchy.
Neither I nor anyone here seems to understand its purpose. The existence of it is likely a significant contributor to peoples' confusion about inout. It's my fault as I should have noticed this getting slipped into the compiler.
Jul 02
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 07/02/2017 10:55 AM, Walter Bright wrote:
 If it is declared as:
 
      inout(char)[] foo(bool condition, inout(char)[] chars);
 
 your specific case will work as expected. Perhaps you meant:
No, it doesn't. The function doesn't compile with that signature. ---- inout(char)[] foo(bool condition, inout(char)[] chars) { if (!condition) return "condition failed!"; /* Error: cannot implicitly convert expression "condition failed!" of type string to inout(char)[] */ return chars; } ----
Jul 02
parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/2/2017 2:34 AM, ag0aep6g wrote:
 On 07/02/2017 10:55 AM, Walter Bright wrote:
 If it is declared as:

      inout(char)[] foo(bool condition, inout(char)[] chars);

 your specific case will work as expected. Perhaps you meant:
No, it doesn't. The function doesn't compile with that signature. ---- inout(char)[] foo(bool condition, inout(char)[] chars) { if (!condition) return "condition failed!"; /* Error: cannot implicitly convert expression "condition failed!" of type string to inout(char)[] */ return chars; } ----
You're right.
Jul 02
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Jul 02
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 15:33, Timon Gehr wrote:
 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:
Also, there are actually at least two other people in this thread who have demonstrated their understanding. So I count two confused people and three people who are not confused. Still not a great ratio, but I'll try to improve it with a blog post.
Jul 02
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/02/2017 10:00 AM, Timon Gehr wrote:
 On 02.07.2017 15:33, Timon Gehr wrote:
 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:
Also, there are actually at least two other people in this thread who have demonstrated their understanding. So I count two confused people and three people who are not confused. Still not a great ratio, but I'll try to improve it with a blog post.
That's fantastic, Timon. Thanks in advance! -- Andrei
Jul 02
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/2/2017 6:33 AM, Timon Gehr wrote:
 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);
Thank you. This explanation makes sense (given that applying const to immutable => immutable).
Jul 02
parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Sunday, 2 July 2017 at 18:49:29 UTC, Walter Bright wrote:
 Thank you. This explanation makes sense (given that applying 
 const to immutable => immutable).
Since seemly everyone is confused about it, this topic looks like a great subject for a blog post.
Jul 02
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/02/2017 04:08 PM, Jack Stouffer wrote:
 On Sunday, 2 July 2017 at 18:49:29 UTC, Walter Bright wrote:
 Thank you. This explanation makes sense (given that applying const to 
 immutable => immutable).
Since seemly everyone is confused about it, this topic looks like a great subject for a blog post.
We have top people on that (i.e. Timon!). Also, I just improved the spec: https://github.com/dlang/dlang.org/pull/1787. Andrei
Jul 02
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02.07.2017 10:12, Sönke Ludwig wrote:
 So I'd probably still opt for simplifying the conversion hierarchy.
It may indeed be a good idea to completely remove inout from the conversion hierarchy in the documentation: const const shared / \ / \ (unqualified) immutable shared The extended hierarchy can be presented in the inout documentation alongside its derivation from the standard conversion hierarchy.
Jul 02
prev sibling parent ag0aep6g <anonymous example.com> writes:
On 07/02/2017 08:39 AM, H. S. Teoh via Digitalmars-d wrote:
 On Sun, Jul 02, 2017 at 06:49:39AM +0200, Timon Gehr via Digitalmars-d wrote:
 On 02.07.2017 06:45, Walter Bright wrote:
 On 7/1/2017 9:12 PM, Timon Gehr wrote:
[...]
 const(inout(char))[] foo(bool condition, inout(char)[] chars){
       if(!condition) return "condition failed!";
       return chars;
 }
[...]
 I think the example demonstrate the reason. It either returns the
 argument or an immutable global. If the argument is immutable, so is
 the return value, otherwise the return value is const.
[...]
 In your example, inout makes no sense at all. It should be written as:
 
 	const(char)[] foo(bool condition, const(char)[] chars)
 
 because if it returns the argument, then it's const(char)[], and if it
 returns an immutable global, then immutable(char)[] implicitly converts
 to const(char)[].
Timon's example makes perfect sense. I don't know if there's an actual need for const(inout), but it enables something you can't do with const: string s = foo(true, "bar");
 Using inout doesn't make sense here because you
 cannot assume that the return value is mutable if the parameter is
 mutable -- the function may return immutable instead.
You don't get a mutable result for a mutable argument, but you get an immutable result for an immutable argument. That's what const(inout) enables.
Jul 02
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/1/2017 3:12 PM, Timon Gehr wrote:
 It used to be the case that const(inout(T)) = const(T),
Anyone want to run git bisect and see when this changed? This would help in figuring out the rationale. Here's the code to test with it: inout(const char)* foo(inout(const(char))* p) { pragma(msg, typeof(p)); static assert(is(typeof(p) == const(char)*)); return p; }
Jul 02
next sibling parent Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Sunday, 2 July 2017 at 09:15:45 UTC, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 It used to be the case that const(inout(T)) = const(T),
Anyone want to run git bisect and see when this changed? This would help in figuring out the rationale. Here's the code to test with it: inout(const char)* foo(inout(const(char))* p) { pragma(msg, typeof(p)); static assert(is(typeof(p) == const(char)*)); return p; }
This code doesn't compile in any version going as far back as 2.038, and before that one this code gets a syntax error. This line: static assert(is(const(inout(int)) == const(int))); stops working after: https://github.com/dlang/dmd/pull/2992 The same pull request changes the type of p in the above example from inout(char)* to inout(const(char))* (it was never const(char)*).
Jul 02
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/2/17 5:15 AM, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 It used to be the case that const(inout(T)) = const(T),
Anyone want to run git bisect and see when this changed? This would help in figuring out the rationale. Here's the code to test with it: inout(const char)* foo(inout(const(char))* p) { pragma(msg, typeof(p)); static assert(is(typeof(p) == const(char)*)); return p; }
https://issues.dlang.org/show_bug.cgi?id=6930 -Steve
Jul 02
parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/2/2017 3:31 AM, Steven Schveighoffer wrote:
 On 7/2/17 5:15 AM, Walter Bright wrote:
 On 7/1/2017 3:12 PM, Timon Gehr wrote:
 It used to be the case that const(inout(T)) = const(T),
Anyone want to run git bisect and see when this changed? This would help in figuring out the rationale. Here's the code to test with it: inout(const char)* foo(inout(const(char))* p) { pragma(msg, typeof(p)); static assert(is(typeof(p) == const(char)*)); return p; }
https://issues.dlang.org/show_bug.cgi?id=6930 -Steve
Ah, thanks to you and Vladimir! This is just what I wanted to see.
Jul 02
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/01/2017 02:47 PM, Andrei Alexandrescu wrote:
 the simplified qualifier hierarcy in
 http://erdani.com/conversions-simplified.svg.
Can't we simplify it by cutting it in half and adding that immutable is implicitly shared? 1) unqualified -> const 2) inout -> const 3) immutable -> const 4) shared is implicit only for immutable
 Is there a need for combining const and inout?
I think there is a reason but only one person knows. :) Ali
Jul 01
prev sibling next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 07/01/2017 11:47 PM, Andrei Alexandrescu wrote:
 Walter looked at http://erdani.com/conversions.svg and said actually 
 "const inout" and "const inout shared" should not exist as distinct 
 qualifier groups, leading to the simplified qualifier hierarcy in 
 http://erdani.com/conversions-simplified.svg.
This may be a stupid question, but those graphs say: inout -> const, but inout may stand for shared and there's no shared -> const. How can inout -> const be allowed while shared -> const is forbidden?
Jul 01
parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Sunday, July 02, 2017 01:16:13 ag0aep6g via Digitalmars-d wrote:
 On 07/01/2017 11:47 PM, Andrei Alexandrescu wrote:
 Walter looked at http://erdani.com/conversions.svg and said actually
 "const inout" and "const inout shared" should not exist as distinct
 qualifier groups, leading to the simplified qualifier hierarcy in
 http://erdani.com/conversions-simplified.svg.
This may be a stupid question, but those graphs say: inout -> const, but inout may stand for shared and there's no shared -> const. How can inout -> const be allowed while shared -> const is forbidden?
It sounds _very_ broken to me if inout can mean shared. After all, the compiler will treat various operations as illegal if a variable is marked shared which would be legal if it weren't. So, if you have an inout parameter accepting a shared argument, then you can break the protections that shared is supposed to be giving. Also, the compiler is supposed to be able to optimize based on the fact that a variable is thread-local and guaranteed not to be shared across threads, and if inout can mean shared, then a function using inout no longer has the guarantee that the data is thread-local and can't optimize based on that inforamtion. Fortunately, it looks like your assertion that shared may stand for inout is wrong, because this code fails to compile: class C { } inout(C) foo(inout(C) c) { return c; } void main() { shared a = new C; auto b = foo(a); } and gives the error test.d(13): Error: function test.foo (inout(C) c) is not callable using argument types (shared(C)) - Jonathan M Davis
Jul 03
parent ag0aep6g <anonymous example.com> writes:
On 07/03/2017 04:01 PM, Jonathan M Davis via Digitalmars-d wrote:
 Fortunately, it looks like your assertion that shared may stand for inout is
 wrong, because this code fails to compile:
 
 class C
 {
 }
 
 inout(C) foo(inout(C) c)
 {
      return c;
 }
 
 void main()
 {
      shared a = new C;
      auto b = foo(a);
 }
 
 and gives the error
 
 test.d(13): Error: function test.foo (inout(C) c) is not callable using
 argument types (shared(C))
Thanks, Jonathan. You're absolutely right. Checking the spec, it even says: "Note: Shared types are not overlooked. Shared types cannot be matched with inout." [1] I think I've only tested with something like `foo(new shared C)`. That is accepted, but not because of inout. The compiler sees that the argument is unique and can't actually be shared with another thread yet, so it strips shared off. Sorry for the noise. Nothing to see here. [1] https://dlang.org/spec/function.html#inout-functions
Jul 03
prev sibling parent deadalnix <deadalnix gmail.com> writes:
On Saturday, 1 July 2017 at 21:47:20 UTC, Andrei Alexandrescu 
wrote:
 Walter looked at http://erdani.com/conversions.svg and said 
 actually "const inout" and "const inout shared" should not 
 exist as distinct qualifier groups, leading to the simplified 
 qualifier hierarcy in 
 http://erdani.com/conversions-simplified.svg.

 Are we missing something? Is there a need for combining const 
 and inout?
Yes. inout == mutable, const or immutable const inout == const or immutable
Jul 03