digitalmars.D - Help needed: immutable struct is a type modifier, and that's wrong and
- FeepingCreature (18/18) Mar 13 2020 See this bug: https://issues.dlang.org/show_bug.cgi?id=20670
- FeepingCreature (8/8) Mar 13 2020 https://gist.github.com/FeepingCreature/61d1ead8b744cfa2152849e5b6680bb2
- Jacob Carlborg (20/38) Mar 13 2020 import std;
- FeepingCreature (10/29) Mar 13 2020 Okay, so you're saying there's an immutable modifier on S, but it
- Jacob Carlborg (11/13) Mar 13 2020 Well, it looks like the spec is kind of correct:
- FeepingCreature (20/34) Apr 15 2020 Late answer: the problem is that the storage class *doesn't* just
- RazvanN (19/37) Apr 15 2020 There is a difference between defining a struct as immutable
- Timon Gehr (5/20) Apr 15 2020 So the proposal is to turn `is` into an even more convoluted mess? :)
- RazvanN (8/30) Apr 15 2020 I agree that this is kind of awkward, but things are confusing
- RazvanN (7/32) Apr 15 2020 Oh I see your point now. In the above example S and T are the
- Timon Gehr (8/40) Apr 16 2020 My point was rather that I don't see any reason why the following two
- Steven Schveighoffer (12/53) Apr 16 2020 So you are suggesting a change to current behavior?
See this bug: https://issues.dlang.org/show_bug.cgi?id=20670 immutable struct S { } static if(is(S == immutable T, T)) { static assert(is(S == T)); } So what happens here is that the spec states that immutable struct S is "the same as if every member had been marked immutable." But that's not at all what the compiler actually does: apparently, it just stores S with an immutable modifier. As the code shows, immutable modifiers can be stripped away. This is of course bad, because it means that there's a type "S", but there's also a type "mutable S" that can only be created by template specialization or Unqual, and it can't be assigned to S despite nominally being the exact same type. Help? (For now, I'll probably homebrew an Unqual that uses mixin to figure out when a type has a "fake immutable modifier". But I won't like it.)
Mar 13 2020
https://gist.github.com/FeepingCreature/61d1ead8b744cfa2152849e5b6680bb2 Here's a workaround for the bug. It's a version of `Unqual` that uses a completely different mechanism to get at the qualifier-less type: it looks up a symbol by that name in the surrounding aggregate, or tries to jump to the "parent of the first member". Even though it "works" for our usecases, this clearly does not spark joy.
Mar 13 2020
On Friday, 13 March 2020 at 09:33:18 UTC, FeepingCreature wrote:See this bug: https://issues.dlang.org/show_bug.cgi?id=20670 immutable struct S { } static if(is(S == immutable T, T)) { static assert(is(S == T)); } So what happens here is that the spec states that immutable struct S is "the same as if every member had been marked immutable." But that's not at all what the compiler actually does: apparently, it just stores S with an immutable modifier. As the code shows, immutable modifiers can be stripped away. This is of course bad, because it means that there's a type "S", but there's also a type "mutable S" that can only be created by template specialization or Unqual, and it can't be assigned to S despite nominally being the exact same type. Help? (For now, I'll probably homebrew an Unqual that uses mixin to figure out when a type has a "fake immutable modifier". But I won't like it.)import std; immutable struct S { } void main() { Unqual!S s; Unqual!S s2; pragma(msg, typeof(s)); // will print S s = s2; } The above code will compile, but if you add a member to S, it fails: Error: cannot modify struct instance `s` of type `S` because it contains `const` or `immutable` members Even though it looks like it's possible to strip of the immutable qualifier it will actually not work in practice. If you don't have any members, it doesn't really matter. If you do have members, it will not compile. -- /Jacob Carlborg
Mar 13 2020
On Friday, 13 March 2020 at 13:25:52 UTC, Jacob Carlborg wrote:import std; immutable struct S { } void main() { Unqual!S s; Unqual!S s2; pragma(msg, typeof(s)); // will print S s = s2; } The above code will compile, but if you add a member to S, it fails: Error: cannot modify struct instance `s` of type `S` because it contains `const` or `immutable` members Even though it looks like it's possible to strip of the immutable qualifier it will actually not work in practice. If you don't have any members, it doesn't really matter. If you do have members, it will not compile. -- /Jacob CarlborgOkay, so you're saying there's an immutable modifier on S, but it doesn't do anything because all the fields are immutable too? So the actual problem would be https://issues.dlang.org/show_bug.cgi?id=20671 , where the immutable-stripped type is just different enough to prevent array conversion. In that case, isn't the solution just to ditch the extraneous implicit immutable() entirely? I mean, either it should always be on S or it should never be on S.
Mar 13 2020
On Friday, 13 March 2020 at 14:17:45 UTC, FeepingCreature wrote:Okay, so you're saying there's an immutable modifier on S, but it doesn't do anything because all the fields are immutable too?Well, it looks like the spec is kind of correct: "A struct declaration can have a storage class of const, immutable or shared. It has an equivalent effect as declaring each member of the struct as const, immutable or shared" What happens if the struct doesn't have any members? If there are no members that can be immutable then the struct can't be immutable? I don't know. -- /Jacob Carlborg
Mar 13 2020
On Friday, 13 March 2020 at 14:49:49 UTC, Jacob Carlborg wrote:On Friday, 13 March 2020 at 14:17:45 UTC, FeepingCreature wrote:Late answer: the problem is that the storage class *doesn't* just declare each member as immutable. If you declare ``` immutable struct S { int i; } ``` then this is *not* the same as ``` struct S { immutable int i; } ``` but rather something that behaves similar to ``` private struct S_ { immutable int i; } alias S = immutable(S_); ``` because the compiler can't differentiate between `immutable struct S` and `immutable(S)`. Which results in the problem that `Unqual!S` yields `S_`, which is a type that isn't supposed to exist, or at least not in a end-user visible way.Okay, so you're saying there's an immutable modifier on S, but it doesn't do anything because all the fields are immutable too?Well, it looks like the spec is kind of correct: "A struct declaration can have a storage class of const, immutable or shared. It has an equivalent effect as declaring each member of the struct as const, immutable or shared" What happens if the struct doesn't have any members? If there are no members that can be immutable then the struct can't be immutable? I don't know. -- /Jacob Carlborg
Apr 15 2020
On Friday, 13 March 2020 at 09:33:18 UTC, FeepingCreature wrote:See this bug: https://issues.dlang.org/show_bug.cgi?id=20670 immutable struct S { } static if(is(S == immutable T, T)) { static assert(is(S == T)); } So what happens here is that the spec states that immutable struct S is "the same as if every member had been marked immutable." But that's not at all what the compiler actually does: apparently, it just stores S with an immutable modifier. As the code shows, immutable modifiers can be stripped away. This is of course bad, because it means that there's a type "S", but there's also a type "mutable S" that can only be created by template specialization or Unqual, and it can't be assigned to S despite nominally being the exact same type. Help? (For now, I'll probably homebrew an Unqual that uses mixin to figure out when a type has a "fake immutable modifier". But I won't like it.)There is a difference between defining a struct as immutable (e.g. immutable struct A {}) and declaring an immutable instance of a struct (e.g. immutable S a;). In the first case the immutable acts as a storage class specifier whereas in the second one it is a type constructor. Unqual is used to ditch only type constructors not storage class specifiers. In the case of storage class specifiers there is no way to get rid of them because, as you mentioned, there is no associated type. So from this perspective, I think that [1] should be fixed. Additionally, I think that static if(is(S == immutable T, T)) should also not pass. The correct form should be static if(is(S == immutable)) This way you express the fact that S is an immutably defined type (with a storage specifier), whereas in the first case you test if S is an immutably declared type (with a type constructor). [1]https://issues.dlang.org/show_bug.cgi?id=20670
Apr 15 2020
On 16.04.20 05:55, RazvanN wrote:Additionally, I think that static if(is(S == immutable T, T)) should also not pass. The correct form should be static if(is(S == immutable)) This way you express the fact that S is an immutably defined type (with a storage specifier), whereas in the first case you test if S is an immutably declared type (with a type constructor).So the proposal is to turn `is` into an even more convoluted mess? :) Why should it even be possible to test for `immutable` on the declaration? That seems like an implementation detail as the only thing it achieves is that all members are marked that way.
Apr 15 2020
On Thursday, 16 April 2020 at 04:28:35 UTC, Timon Gehr wrote:On 16.04.20 05:55, RazvanN wrote:I agree that this is kind of awkward, but things are confusing otherwise. Do you agree that in this situation `static if (is(S == immutable T, T))` should not pass? Or should it pass and the `static assert(is(S == T))` should also pass? In that case, T should bind to an empty type, which D, as far as I know does not support at the moment.Additionally, I think that static if(is(S == immutable T, T)) should also not pass. The correct form should be static if(is(S == immutable)) This way you express the fact that S is an immutably defined type (with a storage specifier), whereas in the first case you test if S is an immutably declared type (with a type constructor).So the proposal is to turn `is` into an even more convoluted mess? :) Why should it even be possible to test for `immutable` on the declaration? That seems like an implementation detail as the only thing it achieves is that all members are marked that way.
Apr 15 2020
On Thursday, 16 April 2020 at 06:05:12 UTC, RazvanN wrote:On Thursday, 16 April 2020 at 04:28:35 UTC, Timon Gehr wrote:Oh I see your point now. In the above example S and T are the same type because `immutable immutable S` == `immutable S`. Right, but the fact that an immutably defined struct cannot be stripped of it's qualifier brings some complications here. Anyway, the fact that T is not equal to S is definitely a bug in this situation.On 16.04.20 05:55, RazvanN wrote:Additionally, I think that static if(is(S == immutable T, T)) should also not pass. The correct form should be static if(is(S == immutable)) This way you express the fact that S is an immutably defined type (with a storage specifier), whereas in the first case you test if S is an immutably declared type (with a type constructor).So the proposal is to turn `is` into an even more convoluted mess? :) Why should it even be possible to test for `immutable` on the declaration? That seems like an implementation detail as the only thing it achieves is that all members are marked that way.
Apr 15 2020
On 16.04.20 08:09, RazvanN wrote:On Thursday, 16 April 2020 at 06:05:12 UTC, RazvanN wrote:My point was rather that I don't see any reason why the following two declarations should be treated differently: immutable struct S{ int x; } struct S{ immutable int x; } I think those should be equivalent declarations. Also, I think `is(S==immutable(T),T)` and `is(S==immutable)` should behave the same except that the first one also declares `T`.On Thursday, 16 April 2020 at 04:28:35 UTC, Timon Gehr wrote:Oh I see your point now. In the above example S and T are the same type because `immutable immutable S` == `immutable S`. Right, but the fact that an immutably defined struct cannot be stripped of it's qualifier brings some complications here. Anyway, the fact that T is not equal to S is definitely a bug in this situation.On 16.04.20 05:55, RazvanN wrote:Additionally, I think that static if(is(S == immutable T, T)) should also not pass. The correct form should be static if(is(S == immutable)) This way you express the fact that S is an immutably defined type (with a storage specifier), whereas in the first case you test if S is an immutably declared type (with a type constructor).So the proposal is to turn `is` into an even more convoluted mess? :) Why should it even be possible to test for `immutable` on the declaration? That seems like an implementation detail as the only thing it achieves is that all members are marked that way.
Apr 16 2020
On 4/16/20 11:21 AM, Timon Gehr wrote:On 16.04.20 08:09, RazvanN wrote:So you are suggesting a change to current behavior? immutable struct S {int x; } struct T {immutable int x; } pragma(msg, is(S == immutable)); // true pragma(msg, is(T == immutable)); // falseOn Thursday, 16 April 2020 at 06:05:12 UTC, RazvanN wrote:My point was rather that I don't see any reason why the following two declarations should be treated differently: immutable struct S{ int x; } struct S{ immutable int x; }On Thursday, 16 April 2020 at 04:28:35 UTC, Timon Gehr wrote:Oh I see your point now. In the above example S and T are the same type because `immutable immutable S` == `immutable S`. Right, but the fact that an immutably defined struct cannot be stripped of it's qualifier brings some complications here. Anyway, the fact that T is not equal to S is definitely a bug in this situation.On 16.04.20 05:55, RazvanN wrote:Additionally, I think that static if(is(S == immutable T, T)) should also not pass. The correct form should be static if(is(S == immutable)) This way you express the fact that S is an immutably defined type (with a storage specifier), whereas in the first case you test if S is an immutably declared type (with a type constructor).So the proposal is to turn `is` into an even more convoluted mess? :) Why should it even be possible to test for `immutable` on the declaration? That seems like an implementation detail as the only thing it achieves is that all members are marked that way.I think those should be equivalent declarations.It makes sense. But in practice, I'm sure people make an assumption based on whether something is immutable and don't check whether an struct has all immutable members, even though semantically the two are equivalent. We would need something in std.traits that does what is needed. -Steve
Apr 16 2020