Re: recursive template expansion: Why does this not compile?
On Wednesday, 21 March 2018 at 23:05:22 UTC, Jonathan M Davis wrote: On Wednesday, March 21, 2018 22:50:32 Ontonator via Digitalmars-d-learn wrote: On Wednesday, 21 March 2018 at 06:39:22 UTC, ag0aep6g wrote: > On 03/21/2018 01:47 AM, Ontonator wrote: >> The following code does not compile: >>> [...] >> >> It gives the error: >>> [...] >> >> The aliases do not have to be aliases, as long as there is >> some reference to the class (e.g. method and variable >> declarations also work). What exactly is the reason for >> this error? > > Compiler bug. It works when you move the declaration of `B` > before the one of `A`. Order shouldn't matter there. Is this a known bug, or should I report it? If you can't find it searching on bugzilla, report it. - Jonathan M Davis https://issues.dlang.org/show_bug.cgi?id=18646
Re: recursive template expansion: Why does this not compile?
On Wednesday, March 21, 2018 22:50:32 Ontonator via Digitalmars-d-learn wrote: > On Wednesday, 21 March 2018 at 06:39:22 UTC, ag0aep6g wrote: > > On 03/21/2018 01:47 AM, Ontonator wrote: > >> The following code does not compile: > >>> [...] > >> > >> It gives the error: > >>> [...] > >> > >> The aliases do not have to be aliases, as long as there is > >> some reference to the class (e.g. method and variable > >> declarations also work). What exactly is the reason for this > >> error? > > > > Compiler bug. It works when you move the declaration of `B` > > before the one of `A`. Order shouldn't matter there. > > Is this a known bug, or should I report it? If you can't find it searching on bugzilla, report it. - Jonathan M Davis
Re: recursive template expansion: Why does this not compile?
On Wednesday, 21 March 2018 at 06:39:22 UTC, ag0aep6g wrote: On 03/21/2018 01:47 AM, Ontonator wrote: The following code does not compile: [...] It gives the error: [...] The aliases do not have to be aliases, as long as there is some reference to the class (e.g. method and variable declarations also work). What exactly is the reason for this error? Compiler bug. It works when you move the declaration of `B` before the one of `A`. Order shouldn't matter there. Is this a known bug, or should I report it?
Re: recursive template expansion: Why does this not compile?
On 03/21/2018 01:47 AM, Ontonator wrote: The following code does not compile: void main() {} class SuperClass {} class TemplatedClass(T : SuperClass) {} class A : SuperClass { alias T = TemplatedClass!B; } class B : SuperClass { alias T = TemplatedClass!C; } class C : SuperClass {} It gives the error: test.d(12): Error: class `test.TemplatedClass(T : SuperClass)` recursive template expansion test.d(12): while looking for match for TemplatedClass!(C) The aliases do not have to be aliases, as long as there is some reference to the class (e.g. method and variable declarations also work). What exactly is the reason for this error? Compiler bug. It works when you move the declaration of `B` before the one of `A`. Order shouldn't matter there.
Re: recursive template expansion: Why does this not compile?
On Wednesday, March 21, 2018 00:47:18 Ontonator via Digitalmars-d-learn wrote: > The following code does not compile: > > void main() {} > > > > class SuperClass {} > > > > class TemplatedClass(T : SuperClass) {} > > > > class A : SuperClass { > > > > alias T = TemplatedClass!B; > > > > } > > > > class B : SuperClass { > > > > alias T = TemplatedClass!C; > > > > } > > > > class C : SuperClass {} > > It gives the error: > > test.d(12): Error: class `test.TemplatedClass(T : SuperClass)` > > recursive template expansion > > test.d(12):while looking for match for > > TemplatedClass!(C) > > The aliases do not have to be aliases, as long as there is some > reference to the class (e.g. method and variable declarations > also work). What exactly is the reason for this error? I'm not sure exactly what's happening, since I'm not very familiar with the exactly how template specializations are defined, but the problem clearly relates to the fact that you used a template specialization instead of a template constraint. If you change TemplatedClass to class TemplatedClass(T) if(is(T : SuperClass)) {} then the code compiles. - Jonathan M Davis
recursive template expansion: Why does this not compile?
The following code does not compile: void main() {} class SuperClass {} class TemplatedClass(T : SuperClass) {} class A : SuperClass { alias T = TemplatedClass!B; } class B : SuperClass { alias T = TemplatedClass!C; } class C : SuperClass {} It gives the error: test.d(12): Error: class `test.TemplatedClass(T : SuperClass)` recursive template expansion test.d(12):while looking for match for TemplatedClass!(C) The aliases do not have to be aliases, as long as there is some reference to the class (e.g. method and variable declarations also work). What exactly is the reason for this error?
Re: Why does this not compile?
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
Re: Why does this not compile?
On 06.03.2018 17:19, Steven Schveighoffer wrote: 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? } 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. Here's how to break immutability: void main(){ int i = 0; struct S{ const(int)* fun()const pure{ return } } 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=18567
Re: Why does this not compile?
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: On 3/6/18 8:42 AM, Simen Kjærås wrote: 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. 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? } 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. 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
Re: Why does this not compile?
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
Re: Why does this not compile?
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
Re: Why does this not compile?
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: 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. 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. -- Simen
Re: Why does this not compile?
Filed issue 18563 Shachar
Re: Why does this not compile?
On 06/03/18 16:06, Steven Schveighoffer wrote: On 3/6/18 8:56 AM, Steven Schveighoffer wrote: 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 For what it's worth, I vote for option #2 (it isn't const even if the 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.
Re: Why does this not compile?
On 3/6/18 8:56 AM, Steven Schveighoffer wrote: 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. 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
Re: Why does this not compile?
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
Re: Why does this not compile?
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: 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 S Looks like a bug to me - please file one in bugzilla. Nope. It's not a bug. S contains a pointer, namely the context pointer for main. 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); } -- Simen
Re: Why does this not compile?
On 3/6/18 7:44 AM, Shachar Shemesh wrote: On 06/03/18 14:00, Steven Schveighoffer wrote: 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: 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 Looks like a bug to me - please file one in bugzilla. Nope. It's not a bug. S contains a pointer, namely the context pointer for main. https://dlang.org/spec/struct.html#nested Which does not explain why my code compiles if I remove the destructor. 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 } -Steve
Re: Why does this not compile?
On 06/03/18 14:00, Steven Schveighoffer wrote: 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: 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 Looks like a bug to me - please file one in bugzilla. Nope. It's not a bug. S contains a pointer, namely the context pointer for main. https://dlang.org/spec/struct.html#nested Which does not explain why my code compiles if I remove the destructor. Shachar
Re: Why does this not compile?
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: 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 Looks like a bug to me - please file one in bugzilla. 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 } -Steve
Re: Why does this not compile?
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
Re: Why does this not compile?
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 S Looks like a bug to me - please file one in bugzilla. -- Simen
Re: Why does this not compile?
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;
Why does this not compile?
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
Why does it not compile?
import std.stdio; void Foo(T:T*)(T arg) if(!is(T==int)) { writeln(arg of Foo:, arg, typeid(T)); } void Foo(T:T*)(T arg) if(is(T==int)) { writeln(int Foo!); } void main() { Foo!(long*)(54); }
Re: Why does it not compile?
Morlan: ... This compiles, 54 is an int: import std.stdio; void Foo(T: T*)(T arg) if(!is(T == int)) { writeln(Arg of Foo: , arg, , typeid(T)); } void Foo(T: T*)(T arg) if(is(T == int)) { writeln(int Foo!); } void main() { Foo!(long*)(54L); } Generally for questions like this, there is the D.learn newsgroup. Bye, bearophile
Re: Why does it not compile?
I did not ask what to do to compile it. I knew that 54L would do. The problem is that in the example I explicitely specify the template parameter as long* so there is no reason for the compiler to try and guess T from the type of the function argument. There is something wrong here.
Re: Why does it not compile?
Am 24.03.2011 11:49, schrieb Morlan: I did not ask what to do to compile it. I knew that 54L would do. The problem is that in the example I explicitely specify the template parameter as long* so there is no reason for the compiler to try and guess T from the type of the function argument. There is something wrong here. I agree. void fun(long l) {} void main() { long foo = 54; fun(42); } works, so that should work without an explicit cast as well. Cheers, - Daniel
Re: Why does it not compile?
The program below compiles. Clearly the if constraints in my original example are causing trouble. It seems like a bug to me. import std.stdio; void Foo(T:T*)(T arg) { writeln(arg of Foo:, arg, typeid(T)); } void main() { Foo!(long*)(54); }
Re: Why does it not compile?
Morlan: I did not ask what to do to compile it. I knew that 54L would do. The problem is that in the example I explicitely specify the template parameter as long* so there is no reason for the compiler to try and guess T from the type of the function argument. There is something wrong here. The compiler is not guessing the type here. It's just that the type the template is explicitly instantiated with, and the type T of the data, aren't the same. You see it better with this simpler example: import std.stdio; void foo(T)(T x) if(is(T == int)) { writeln(1); } void foo(T)(T x) if(!is(T == int)) { writeln(2); } void main() { foo(1); // 1 foo(1L); // 2 foo!(int)(1); // 1 foo!(long)(1L); // 2 foo!(long)(1); // error foo!(int)(1L); // error } Bye, bearophile