digitalmars.D - Why does this not compile?
- Shachar Shemesh (30/30) Mar 06 2018 void main() {
- Diego (9/18) Mar 06 2018 You cannot assign a const element (`a`) to a non-const element
- Shachar Shemesh (4/7) Mar 06 2018 Sure you can. During assignment you are making a copy. Why do you care
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (4/15) Mar 06 2018 Looks like a bug to me - please file one in bugzilla.
- Steven Schveighoffer (13/30) Mar 06 2018 Nope. It's not a bug. S contains a pointer, namely the context pointer
- Shachar Shemesh (3/26) Mar 06 2018 Which does not explain why my code compiles if I remove the destructor.
- Steven Schveighoffer (10/36) Mar 06 2018 If the struct is POD, there is no need to include the context pointer.
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (27/48) Mar 06 2018 It's a bug. As pointed out elsewhere in this thread, it compiles
- Steven Schveighoffer (6/30) Mar 06 2018 That, I would consider a bug. If it's not, then definitely, you should
- Steven Schveighoffer (6/38) Mar 06 2018 There is a third possibility:
- Shachar Shemesh (30/41) Mar 06 2018 For what it's worth, I vote for option #2 (it isn't const even if the
- Steven Schveighoffer (13/22) Mar 06 2018 These aren't globals or static, they are no different than pointers
- Shachar Shemesh (2/2) Mar 06 2018 Filed issue 18563
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (27/46) Mar 06 2018 immutable throws a wrench in the works for the idea that the
- Shachar Shemesh (6/9) Mar 06 2018 This just means it is completely and totally broken. Changing the
- Steven Schveighoffer (23/69) Mar 06 2018 That's not a demonstration of breaking immutability. counter-proof:
- Timon Gehr (23/50) Mar 06 2018 Here's how to break immutability:
- Steven Schveighoffer (6/9) Mar 07 2018 Yeah, the more I think about it, the more I think the context pointer
void main() { struct S { uint value; ~this() { } } const S a = S(12); S b = a; } test.d(10): Error: cannot implicitly convert expression a of type const(S) to S Doing *any* of the following makes the code compile: * Making the struct "static" * Making the struct global (essentially same as above) * Removing the struct's destructor I can kinda see why it won't compile without making it static. There is a hidden pointer to the frame that is const, implying the frame is also const. This constness would be overridden if the assignment is allowed to go through. I don't think this is a very good reason (see below), but I understand it. What I do not understand is why removing the destructor solves the error. While I get while the compiler treats the frame pointer as const, I should point out that if I add a static variable to the struct, that one remains mutable even when the instance itself is const. There is no inherent difference between a variable stored as static and a variable stored in the context frame. And before you answer with "in D pointer constness is transitive", allow me to point something out: It is not possible to ever change the frame pointer of a struct. That pointer is, effectively, always const. Shachar
Mar 06 2018
On Tuesday, 6 March 2018 at 10:03:54 UTC, Shachar Shemesh wrote:void main() { struct S { uint value; ~this() { } } const S a = S(12); S b = a; }You cannot assign a const element (`a`) to a non-const element (`b`) in `S b = a` expression. To make de assignment, you have to cast a to a non-constant expression: S b = cast(S)a; Or make `b` as const: const S b = a; Or, better, use auto keyword: auto b = a;
Mar 06 2018
On 06/03/18 12:16, Diego wrote:You cannot assign a const element (`a`) to a non-const element (`b`) in `S b = a` expression.Sure you can. During assignment you are making a copy. Why do you care whether the original is const? Shachar
Mar 06 2018
On Tuesday, 6 March 2018 at 10:03:54 UTC, Shachar Shemesh wrote:void main() { struct S { uint value; ~this() { } } const S a = S(12); S b = a; } test.d(10): Error: cannot implicitly convert expression a of type const(S) to SLooks like a bug to me - please file one in bugzilla. -- Simen
Mar 06 2018
On 3/6/18 6:21 AM, Simen Kjærås wrote:On Tuesday, 6 March 2018 at 10:03:54 UTC, Shachar Shemesh wrote:Nope. It's not a bug. S contains a pointer, namely the context pointer for main. https://dlang.org/spec/struct.html#nested Try this: void main() { static struct S { uint value; ~this() {} } ... // rest of your code } -Stevevoid main() { struct S { uint value; ~this() { } } const S a = S(12); S b = a; } test.d(10): Error: cannot implicitly convert expression a of type const(S) to SLooks like a bug to me - please file one in bugzilla.
Mar 06 2018
On 06/03/18 14:00, Steven Schveighoffer wrote:On 3/6/18 6:21 AM, Simen Kjærås wrote:Which does not explain why my code compiles if I remove the destructor. ShacharOn Tuesday, 6 March 2018 at 10:03:54 UTC, Shachar Shemesh wrote:Nope. It's not a bug. S contains a pointer, namely the context pointer for main. https://dlang.org/spec/struct.html#nestedvoid main() { struct S { uint value; ~this() { } } const S a = S(12); S b = a; } test.d(10): Error: cannot implicitly convert expression a of type const(S) to SLooks like a bug to me - please file one in bugzilla.
Mar 06 2018
On 3/6/18 7:44 AM, Shachar Shemesh wrote:On 06/03/18 14:00, Steven Schveighoffer wrote:If the struct is POD, there is no need to include the context pointer. void main() { struct POD { int x; } pragma(msg, POD.sizeof); // 4 struct Nested { int x; void someMember() {} } pragma(msg, Nested.sizeof); // 16 } -SteveOn 3/6/18 6:21 AM, Simen Kjærås wrote:Which does not explain why my code compiles if I remove the destructor.On Tuesday, 6 March 2018 at 10:03:54 UTC, Shachar Shemesh wrote:Nope. It's not a bug. S contains a pointer, namely the context pointer for main. https://dlang.org/spec/struct.html#nestedvoid main() { struct S { uint value; ~this() { } } const S a = S(12); S b = a; } test.d(10): Error: cannot implicitly convert expression a of type const(S) to SLooks like a bug to me - please file one in bugzilla.
Mar 06 2018
On Tuesday, 6 March 2018 at 12:00:43 UTC, Steven Schveighoffer wrote:On 3/6/18 6:21 AM, Simen Kjærås wrote:It's a bug. As pointed out elsewhere in this thread, it compiles correctly when there's no destructor. Essentially, this bug is caused by the context pointer being typed as void*, and becoming (of course) const(void*) for a const(S). If it'd been const(void)* in the first place, Shachar's code would have compiled and worked correctly. Is it misleading for the context pointer to be const(void)*? In a way, maybe. However, it's opaquely typed, and its constness says nothing about what's on the other end. Also, the language completely disregards the constness in any case: unittest { int i = 0; struct S { int n; void fun() const { i++; } } const S s; assert(i == 0); s.fun(); assert(i == 1); } -- SimenOn Tuesday, 6 March 2018 at 10:03:54 UTC, Shachar Shemesh wrote:Nope. It's not a bug. S contains a pointer, namely the context pointer for main.void main() { struct S { uint value; ~this() { } } const S a = S(12); S b = a; } test.d(10): Error: cannot implicitly convert expression a of type const(S) to SLooks like a bug to me - please file one in bugzilla.
Mar 06 2018
On 3/6/18 8:42 AM, Simen Kjærås wrote:It's a bug. As pointed out elsewhere in this thread, it compiles correctly when there's no destructor. Essentially, this bug is caused by the context pointer being typed as void*, and becoming (of course) const(void*) for a const(S). If it'd been const(void)* in the first place, Shachar's code would have compiled and worked correctly. Is it misleading for the context pointer to be const(void)*? In a way, maybe. However, it's opaquely typed, and its constness says nothing about what's on the other end. Also, the language completely disregards the constness in any case: unittest { int i = 0; struct S { int n; void fun() const { i++; } } const S s; assert(i == 0); s.fun(); assert(i == 1); }That, I would consider a bug. If it's not, then definitely, you should be able to implicitly cast to/from const. So a bug report is in order. It should be decided one way or another -- either the context pointer is part of the struct type or it isn't. -Steve
Mar 06 2018
On 3/6/18 8:56 AM, Steven Schveighoffer wrote:On 3/6/18 8:42 AM, Simen Kjærås wrote:There is a third possibility: It's part of the type AND it's typed as const if it can be (i.e. none of the methods in the struct modify the context data, and any use of the context data implicitly casts from const). -SteveIt's a bug. As pointed out elsewhere in this thread, it compiles correctly when there's no destructor. Essentially, this bug is caused by the context pointer being typed as void*, and becoming (of course) const(void*) for a const(S). If it'd been const(void)* in the first place, Shachar's code would have compiled and worked correctly. Is it misleading for the context pointer to be const(void)*? In a way, maybe. However, it's opaquely typed, and its constness says nothing about what's on the other end. Also, the language completely disregards the constness in any case: unittest { int i = 0; struct S { int n; void fun() const { i++; } } const S s; assert(i == 0); s.fun(); assert(i == 1); }That, I would consider a bug. If it's not, then definitely, you should be able to implicitly cast to/from const. So a bug report is in order. It should be decided one way or another -- either the context pointer is part of the struct type or it isn't.
Mar 06 2018
On 06/03/18 16:06, Steven Schveighoffer wrote:On 3/6/18 8:56 AM, Steven Schveighoffer wrote:struct is), for the same reason that const structs can modify static variables - it's not part of the struct. I'll phrase is another way, if this shouldn't compile: unittest { int i; struct S { int a; void func() const { ++i; } } } Then neither should this: struct S { static int i; int a; void func() const { ++i; } } I fail to see any reasoning[1] that disallows the former but allows the later. Shachar 1 - That is obviously not true. I see the reasoning all too well. Static vars are like globals, and you're not getting to them via the struct's instance. I would argue that, as far as the programmer, however, there is no difference. There is no semantic difference between allowing the first and not the second.So a bug report is in order. It should be decided one way or another -- either the context pointer is part of the struct type or it isn't.There is a third possibility: It's part of the type AND it's typed as const if it can be (i.e. none of the methods in the struct modify the context data, and any use of the context data implicitly casts from const). -Steve
Mar 06 2018
On 3/6/18 9:42 AM, Shachar Shemesh wrote:I fail to see any reasoning[1] that disallows the former but allows the later. 1 - That is obviously not true. I see the reasoning all too well. Static vars are like globals, and you're not getting to them via the struct's instance. I would argue that, as far as the programmer, however, there is no difference. There is no semantic difference between allowing the first and not the second.These aren't globals or static, they are no different than pointers (which is actually what they are). Here is the problem with option 2: If you have a struct that is immutable (or shared), it can be passed without problems to another thread. If you can then start mucking with local variables in another thread with the assumption that they are thread-local, then you have broken the guarantee of immutable or shared. Even const can cause problems. static data is different, in that it is either shared or thread-local, and then the path forward is quite clear. Passing thread-local data to another thread as if it were local to the recipient is like __gshared, and shouldn't be so easy to do. -Steve
Mar 06 2018
On Tuesday, 6 March 2018 at 13:56:30 UTC, Steven Schveighoffer wrote:On 3/6/18 8:42 AM, Simen Kjærås wrote:immutable throws a wrench in the works for the idea that the context pointer is part of the struct. Consider the exact same example, but with immutable(S) instead of const(S). IMO, this indicates the context is not part of the struct (though the context *pointer* arguably is). And just in case anyone doesn't immediately see how even disallowing the above would not solve the immutable problem: unittest { int n = 0; struct S { int fun() const { return n; } } immutable S s; assert(s.fun == 0); n++; assert(s.fun == 1); // Not so immutable now, are you? } Interestingly, replacing 'const' with 'immutable' on fun gives a compilation error: "immutable function 'foo.__unittest_foo_1_0.S.fun' cannot access mutable data 'n'". This seems even weirder to me, but can certainly be taken as evidence in favor of your view. The same error message does *not* show up if n is instead a global variable. -- Simenunittest { int i = 0; struct S { int n; void fun() const { i++; } } const S s; assert(i == 0); s.fun(); assert(i == 1); }That, I would consider a bug. If it's not, then definitely, you should be able to implicitly cast to/from const. So a bug report is in order. It should be decided one way or another -- either the context pointer is part of the struct type or it isn't.
Mar 06 2018
On 06/03/18 17:00, Simen Kjærås wrote:Interestingly, replacing 'const' with 'immutable' on fun gives a compilation error: "immutable function 'foo.__unittest_foo_1_0.S.fun' cannot access mutable data 'n'".This just means it is completely and totally broken. Changing the "const" to "immutable" merely decreases the types that can be passed to the function. I see no case where it is legitimate for a const decoration to compile and an immutable one not. Shachar
Mar 06 2018
On 3/6/18 10:00 AM, Simen Kjærås wrote:On Tuesday, 6 March 2018 at 13:56:30 UTC, Steven Schveighoffer wrote:That's not a demonstration of breaking immutability. counter-proof: struct S { static int x; int fun() const { return x; } } immutable S s; assert(s.fun == 0); S.x++; assert(s.fun == 1); In other words, if the struct can access the data considered "outside it's instance", then it it can return it, change it even. However, this is not a question that has been answered yet. The compiler clearly treats the context pointer as part of the instance in some cases, and not part of the instance in other cases. Note that if the decision comes down that the context pointer is part of the instance, even *creating* an immutable S should be disallowed, as it clearly allows both mutable and immutable references.On 3/6/18 8:42 AM, Simen Kjærås wrote:immutable throws a wrench in the works for the idea that the context pointer is part of the struct. Consider the exact same example, but with immutable(S) instead of const(S). IMO, this indicates the context is not part of the struct (though the context *pointer* arguably is). And just in case anyone doesn't immediately see how even disallowing the above would not solve the immutable problem: unittest { int n = 0; struct S { int fun() const { return n; } } immutable S s; assert(s.fun == 0); n++; assert(s.fun == 1); // Not so immutable now, are you? }unittest { int i = 0; struct S { int n; void fun() const { i++; } } const S s; assert(i == 0); s.fun(); assert(i == 1); }That, I would consider a bug. If it's not, then definitely, you should be able to implicitly cast to/from const. So a bug report is in order. It should be decided one way or another -- either the context pointer is part of the struct type or it isn't.Interestingly, replacing 'const' with 'immutable' on fun gives a compilation error: "immutable function 'foo.__unittest_foo_1_0.S.fun' cannot access mutable data 'n'".Wat... This is like taking both sides at once (it's both part of the instance and not part of the instance).This seems even weirder to me, but can certainly be taken as evidence in favor of your view. The same error message does *not* show up if n is instead a global variable.This is very broken. And it needs attention. -Steve
Mar 06 2018
On 06.03.2018 17:19, Steven Schveighoffer wrote:Here's how to break immutability: void main(){ int i = 0; struct S{ const(int)* fun()const pure{ return &i; } } immutable S s; static const(int)* foo(immutable(S) s)pure{ return s.fun(); } immutable(int) *pi=foo(s); import std.stdio; writeln(*pi); // 0 i+=1; writeln(*pi); // 1 } The reasoning "the reference is stored in the type yet not part of the type" does not work for pure functions, as then you cannot offer an alternative explanation in terms of an external data store. https://issues.dlang.org/show_bug.cgi?id=18567unittest { int n = 0; struct S { int fun() const { return n; } } immutable S s; assert(s.fun == 0); n++; assert(s.fun == 1); // Not so immutable now, are you? }That's not a demonstration of breaking immutability. counter-proof: struct S { static int x; int fun() const { return x; } } immutable S s; assert(s.fun == 0); S.x++; assert(s.fun == 1); In other words, if the struct can access the data considered "outside it's instance", then it it can return it, change it even.
Mar 06 2018
On 3/6/18 5:27 PM, Timon Gehr wrote:The reasoning "the reference is stored in the type yet not part of the type" does not work for pure functions, as then you cannot offer an alternative explanation in terms of an external data store.Yeah, the more I think about it, the more I think the context pointer simply needs to be part of the instance in the type system. I posted a threading issue elsewhere. The problem is, correcting the bug will break a lot of code. -Steve
Mar 07 2018