digitalmars.D - Why do "const inout" and "const inout shared" exist?
- Andrei Alexandrescu (7/7) Jul 01 2017 Walter looked at http://erdani.com/conversions.svg and said actually
- Stefan Koch (3/12) Jul 01 2017 inout is bascially the same as const for all parctical purposes.
- Timon Gehr (15/29) Jul 01 2017 struct S{
- Stefan Koch (3/17) Jul 01 2017 Oh damn. I was not aware that it could behave non-constly.
- Timon Gehr (5/25) Jul 01 2017 Since the beginning. :)
- Timon Gehr (14/22) Jul 01 2017 In DMD's implementation, yes. (Combinations of qualifiers are
- Andrei Alexandrescu (11/39) Jul 01 2017 Thanks! Well I do want to have a hierarchy with all possible qualifier
- Timon Gehr (9/21) Jul 01 2017 This means that there can be aliasing between an unqualified reference
- Andrei Alexandrescu (4/22) Jul 02 2017 Well const shared exists already with the semantics of "you can't modify...
- Timon Gehr (2/25) Jul 02 2017 If the data is not written atomically, how can it be loaded atomically?
- Andrei Alexandrescu (3/30) Jul 02 2017 Then you atomically load parts of it, the point being that the matter is...
- Timon Gehr (5/36) Jul 02 2017 In general, depending on the hardware memory model and the language
- Andrei Alexandrescu (18/22) Jul 02 2017 Yes, there must be a handshake. Oh, I see your point. Let me illustrate:
- Andrei Alexandrescu (4/5) Jul 02 2017 I meant:
- Andrei Alexandrescu (3/9) Jul 02 2017 Dognabbit. No, I meant the previous one! The pointer itself is private
- Timon Gehr (3/30) Jul 02 2017 Yes, I think there is no way to collapse it without bad consequences.
- Stefan Koch (3/45) Jul 01 2017 I cannot think so a single time I ever used const inout.
- Timon Gehr (3/6) Jul 01 2017 I'll boldly claim that this is true mostly because you have never used
- Stefan Koch (2/8) Jul 01 2017 affirmative
- Walter Bright (3/4) Jul 01 2017 The math needs to work whether it is ever used or not, otherwise we wind...
- Stefan Koch (4/8) Jul 01 2017 I agree.
- Shachar Shemesh (9/13) Jul 01 2017 I don't see how it can. They provide different guarantees. If anything,
- Andrei Alexandrescu (8/19) Jul 02 2017 That supports the case for allowing the conversion.
- Shachar Shemesh (15/25) Jul 02 2017 It does, with two (or is that three?) caveats.
- Andrei Alexandrescu (6/14) Jul 02 2017 Allowing the conversion does not preclude any optimization; after all
- Walter Bright (7/16) Jul 02 2017 Pointer aliasing is indeed a big deal for optimization.
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/8) Jul 03 2017 You probably meant mutable, but how does that bring any
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/8) Jul 03 2017 I also think it would be a good idea for the D crowd to be
- Petar Kirov [ZombineDev] (28/36) Jul 03 2017 Unlike C++, in D objects can't be shared across threads, unless
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (13/25) Jul 03 2017 I understand the intent, but since everything is "shared" in C++,
- Walter Bright (4/28) Jul 03 2017 Keep in mind that today's optimizers are pretty much tuned to what works...
- H. S. Teoh via Digitalmars-d (6/9) Jul 03 2017 So what stops us from actually exploiting them in dmd's optimizer?
- Walter Bright (2/3) Jul 04 2017 Nothing. Pull requests are welcome!
- H. S. Teoh via Digitalmars-d (8/12) Jul 05 2017 Sure, but it would be helpful if we knew what are some of the things
- Nick Treleaven (6/9) Jul 03 2017 In safe Rust, a reference to mutable data has to be unique [1].
- Timon Gehr (16/38) Jul 02 2017 This is not the whole story. The best way to think about 'shared' is
- Walter Bright (2/9) Jul 01 2017 If const(inout(T)) is reduced to inout(T), it works.
- Timon Gehr (8/19) Jul 01 2017 Counterexample:
- Walter Bright (3/24) Jul 01 2017 I don't think that matters. There's no reason to write const(inout) for ...
- Timon Gehr (4/30) Jul 01 2017 I think the example demonstrate the reason. It either returns the
- Walter Bright (9/40) Jul 01 2017 The purpose of inout is to transmit the mutable/const/immutable attribut...
- H. S. Teoh via Digitalmars-d (24/56) Jul 01 2017 Whoa, wait a second here. Isn't this completely over-engineering
- =?UTF-8?Q?S=c3=b6nke_Ludwig?= (11/24) Jul 02 2017 The idea is to be able to write: string result = foo(true, "bar");
- Walter Bright (12/17) Jul 02 2017 If it is declared as:
- ag0aep6g (11/16) Jul 02 2017 No, it doesn't. The function doesn't compile with that signature.
- Walter Bright (2/20) Jul 02 2017 You're right.
- Timon Gehr (23/33) Jul 02 2017 The opposite is true. I understand it, and you seem to understand it
- Timon Gehr (5/10) Jul 02 2017 Also, there are actually at least two other people in this thread who
- Andrei Alexandrescu (2/13) Jul 02 2017 That's fantastic, Timon. Thanks in advance! -- Andrei
- Walter Bright (3/25) Jul 02 2017 Thank you. This explanation makes sense (given that applying const to im...
- Jack Stouffer (3/5) Jul 02 2017 Since seemly everyone is confused about it, this topic looks like
- Andrei Alexandrescu (5/11) Jul 02 2017 We have top people on that (i.e. Timon!).
- Timon Gehr (8/9) Jul 02 2017 It may indeed be a good idea to completely remove inout from the
- ag0aep6g (10/31) Jul 02 2017 [...]
- Walter Bright (10/11) Jul 02 2017 Anyone want to run git bisect and see when this changed? This would help...
- Vladimir Panteleev (10/21) Jul 02 2017 This code doesn't compile in any version going as far back as
- Steven Schveighoffer (3/17) Jul 02 2017 https://issues.dlang.org/show_bug.cgi?id=6930
- Walter Bright (2/21) Jul 02 2017 Ah, thanks to you and Vladimir! This is just what I wanted to see.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (9/12) Jul 01 2017 Can't we simplify it by cutting it in half and adding that immutable is
- ag0aep6g (4/8) Jul 01 2017 This may be a stupid question, but those graphs say: inout -> const, but...
- Jonathan M Davis via Digitalmars-d (28/36) Jul 03 2017 It sounds _very_ broken to me if inout can mean shared. After all, the
- ag0aep6g (10/32) Jul 03 2017 Thanks, Jonathan. You're absolutely right. Checking the spec, it even
- deadalnix (5/12) Jul 03 2017 Yes.
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 2017
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, Andreiinout is bascially the same as const for all parctical purposes.
Jul 01 2017
On 02.07.2017 00:10, Stefan Koch wrote:On Saturday, 1 July 2017 at 21:47:20 UTC, Andrei Alexandrescu 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 }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, Andreiinout is bascially the same as const for all parctical purposes.
Jul 01 2017
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 2017
On 02.07.2017 00:26, Stefan Koch wrote:On Saturday, 1 July 2017 at 22:16:12 UTC, Timon Gehr wrote: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.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 2017
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 2017
On 07/01/2017 06:12 PM, Timon Gehr wrote:On 01.07.2017 23:47, 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. 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. AndreiWalter 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 2017
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 2017
On 07/01/2017 07:55 PM, Timon Gehr wrote:On 02.07.2017 01:08, Andrei Alexandrescu wrote: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. -- AndreiVaguely 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 02 2017
On 02.07.2017 14:41, Andrei Alexandrescu wrote:On 07/01/2017 07:55 PM, Timon Gehr wrote:If the data is not written atomically, how can it be loaded atomically?On 02.07.2017 01:08, Andrei Alexandrescu wrote: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. -- AndreiVaguely 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 02 2017
On 07/02/2017 09:07 AM, Timon Gehr wrote:On 02.07.2017 14:41, Andrei Alexandrescu wrote: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. -- AndreiOn 07/01/2017 07:55 PM, Timon Gehr wrote:If the data is not written atomically, how can it be loaded atomically?On 02.07.2017 01:08, Andrei Alexandrescu wrote: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. -- AndreiVaguely 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 02 2017
On 02.07.2017 15:29, Andrei Alexandrescu wrote:On 07/02/2017 09:07 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.On 02.07.2017 14:41, Andrei Alexandrescu wrote: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. -- AndreiOn 07/01/2017 07:55 PM, Timon Gehr wrote:If the data is not written atomically, how can it be loaded atomically?On 02.07.2017 01:08, Andrei Alexandrescu wrote: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. -- AndreiVaguely 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 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
Jul 02 2017
On 07/02/2017 09:48 AM, Andrei Alexandrescu wrote:*p = 42; // should be a shared write, it's notI meant: p = new int; // should be a shared write, it's not Andrei
Jul 02 2017
On 07/02/2017 09:49 AM, Andrei Alexandrescu wrote:On 07/02/2017 09:48 AM, Andrei Alexandrescu wrote:Dognabbit. No, I meant the previous one! The pointer itself is private to gun. -- Andrei*p = 42; // should be a shared write, it's notI meant: p = new int; // should be a shared write, it's not
Jul 02 2017
On 02.07.2017 15:48, Andrei Alexandrescu wrote:On 07/02/2017 09:39 AM, Timon Gehr wrote:Yes, precisely.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?Yes, I think there is no way to collapse it without bad consequences.
Jul 02 2017
On Saturday, 1 July 2017 at 23:08:40 UTC, Andrei Alexandrescu wrote:On 07/01/2017 06:12 PM, Timon Gehr wrote:I cannot think so a single time I ever used const inout.On 01.07.2017 23:47, 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. 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. AndreiWalter 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 2017
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 2017
On Sunday, 2 July 2017 at 01:51:05 UTC, Timon Gehr wrote:On 02.07.2017 03:22, Stefan Koch wrote:affirmative... 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 2017
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 2017
On Sunday, 2 July 2017 at 03:55:37 UTC, Walter Bright wrote:On 7/1/2017 6:22 PM, Stefan Koch wrote:I agree. I may add though that we already have a few absurdities ... inout itself for example :)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 2017
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 2017
On 07/02/2017 02:49 AM, Shachar Shemesh wrote:On 02/07/17 02:08, 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. AndreiVaguely 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.
Jul 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
Jul 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
Jul 02 2017
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 2017
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 2017
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 2017
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: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. // ... } ```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.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 2017
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 - likeI 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 2017
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 2017
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 2017
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 2017
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: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. KernighanSo what stops us from actually exploiting them in dmd's optimizer?Nothing. Pull requests are welcome!
Jul 05 2017
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 2017
On 02.07.2017 14:31, Andrei Alexandrescu wrote:On 07/02/2017 02:49 AM, Shachar Shemesh wrote: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*.On 02/07/17 02:08, 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.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.
Jul 02 2017
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 2017
On 02.07.2017 05:13, Walter Bright wrote:On 7/1/2017 3:12 PM, Timon Gehr wrote: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.)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 2017
On 7/1/2017 9:12 PM, Timon Gehr wrote:On 02.07.2017 05:13, Walter Bright wrote:I don't think that matters. There's no reason to write const(inout) for a return value.On 7/1/2017 3:12 PM, Timon Gehr wrote: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.)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 2017
On 02.07.2017 06:45, Walter Bright wrote:On 7/1/2017 9:12 PM, Timon Gehr wrote: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.On 02.07.2017 05:13, Walter Bright wrote:I don't think that matters. There's no reason to write const(inout) for a return value.On 7/1/2017 3:12 PM, Timon Gehr wrote: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.)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 2017
On 7/1/2017 9:49 PM, Timon Gehr wrote:On 02.07.2017 06:45, Walter Bright wrote: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.On 7/1/2017 9:12 PM, Timon Gehr wrote: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.On 02.07.2017 05:13, Walter Bright wrote:I don't think that matters. There's no reason to write const(inout) for a return value.On 7/1/2017 3:12 PM, Timon Gehr wrote: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.)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 2017
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: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 LongOn 7/1/2017 9:12 PM, Timon Gehr wrote: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.On 02.07.2017 05:13, Walter Bright wrote:I don't think that matters. There's no reason to write const(inout) for a return value.On 7/1/2017 3:12 PM, Timon Gehr wrote: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.)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 2017
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. TThe 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 2017
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 2017
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 2017
On 7/2/2017 2:34 AM, ag0aep6g wrote:On 07/02/2017 10:55 AM, Walter Bright wrote:You're right.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 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.
Jul 02 2017
On 02.07.2017 15:33, Timon Gehr wrote:On 02.07.2017 10:55, Walter Bright wrote: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.Neither I nor anyone here seems to understand its purpose.The opposite is true. I understand it, and you seem to understand it partially:
Jul 02 2017
On 07/02/2017 10:00 AM, Timon Gehr wrote:On 02.07.2017 15:33, Timon Gehr wrote:That's fantastic, Timon. Thanks in advance! -- AndreiOn 02.07.2017 10:55, Walter Bright wrote: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.Neither I nor anyone here seems to understand its purpose.The opposite is true. I understand it, and you seem to understand it partially:
Jul 02 2017
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 2017
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 2017
On 07/02/2017 04:08 PM, Jack Stouffer wrote:On Sunday, 2 July 2017 at 18:49:29 UTC, Walter Bright wrote:We have top people on that (i.e. Timon!). Also, I just improved the spec: https://github.com/dlang/dlang.org/pull/1787. AndreiThank 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 2017
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 2017
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 2017
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 2017
On Sunday, 2 July 2017 at 09:15:45 UTC, Walter Bright wrote:On 7/1/2017 3:12 PM, Timon Gehr wrote: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)*).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 2017
On 7/2/17 5:15 AM, Walter Bright wrote:On 7/1/2017 3:12 PM, Timon Gehr wrote:https://issues.dlang.org/show_bug.cgi?id=6930 -SteveIt 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 2017
On 7/2/2017 3:31 AM, Steven Schveighoffer wrote:On 7/2/17 5:15 AM, Walter Bright wrote:Ah, thanks to you and Vladimir! This is just what I wanted to see.On 7/1/2017 3:12 PM, Timon Gehr wrote:https://issues.dlang.org/show_bug.cgi?id=6930 -SteveIt 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 2017
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 immutableIs there a need for combining const and inout?I think there is a reason but only one person knows. :) Ali
Jul 01 2017
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 2017
On Sunday, July 02, 2017 01:16:13 ag0aep6g via Digitalmars-d wrote:On 07/01/2017 11:47 PM, Andrei Alexandrescu wrote: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 DavisWalter 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 03 2017
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 2017
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 2017