Re: Mutate immutable inside shared static constructor
On Saturday, 23 March 2024 at 21:59:57 UTC, Nick Treleaven wrote: On Saturday, 23 March 2024 at 21:53:43 UTC, Jonathan M Davis wrote: Yes, it's a bug. It's a clear violation of the type system if a non-mutable variable is ever given a value more than once. It should be initialized, and then it should be treated as illegal to ever assign to it - or to do anything else which would mutate it. So, clearly, the logic in static constructors with regards to non-mutable variables is overly simple at the moment. Thanks, filed: https://issues.dlang.org/show_bug.cgi?id=24449 Thanks for your prompt answer.
Re: Mutate immutable inside shared static constructor
On Saturday, 23 March 2024 at 21:53:43 UTC, Jonathan M Davis wrote: Yes, it's a bug. It's a clear violation of the type system if a non-mutable variable is ever given a value more than once. It should be initialized, and then it should be treated as illegal to ever assign to it - or to do anything else which would mutate it. So, clearly, the logic in static constructors with regards to non-mutable variables is overly simple at the moment. Thanks, filed: https://issues.dlang.org/show_bug.cgi?id=24449
Re: Mutate immutable inside shared static constructor
On Saturday, March 23, 2024 3:23:23 PM MDT Nick Treleaven via Digitalmars-d- learn wrote: > I've not used static constructors before, but it seems like the > following should not be allowed: > > ```d > import std.stdio; > > immutable int x; > > @safe shared static this() > { > x.writeln(); // 0 > x = 5; > x.writeln(); // 5 > x = 6; > x++; > assert(x == 7); > } > ``` > Should I file a bug to require that `x` is only written to once? > That would make it consistent with class constructors: > > ```d > class C > { > immutable int x; > this() > { > x = 5; > x = 6; // error, x initialized multiple times > } > } > ``` Yes, it's a bug. It's a clear violation of the type system if a non-mutable variable is ever given a value more than once. It should be initialized, and then it should be treated as illegal to ever assign to it - or to do anything else which would mutate it. So, clearly, the logic in static constructors with regards to non-mutable variables is overly simple at the moment. - Jonathan M Davis
Mutate immutable inside shared static constructor
I've not used static constructors before, but it seems like the following should not be allowed: ```d import std.stdio; immutable int x; @safe shared static this() { x.writeln(); // 0 x = 5; x.writeln(); // 5 x = 6; x++; assert(x == 7); } ``` Should I file a bug to require that `x` is only written to once? That would make it consistent with class constructors: ```d class C { immutable int x; this() { x = 5; x = 6; // error, x initialized multiple times } } ```
Re: Implicit conversion of string to array of immutable ubytes
On Saturday, March 23, 2024 12:11:15 AM MDT Per Nordlöw via Digitalmars-d- learn wrote: > Why doesn't string implicitly convert to immutable(ubyte)[] in > @safe mode? Why would it? They're different types. Their elements happen to have the same size, but that doesn't mean that they're used for the same thing at all. And having them be implicitly convertible could cause serious problems with overloading. If you want to do that conversion without a cast, then you can just use std.string.representation (which will do the cast internally). - Jonathan M Davis
Re: std.string.assumeUTF() silently casting mutable to immutable?
On Wednesday, 14 February 2024 at 11:56:29 UTC, Forest wrote: On Wednesday, 14 February 2024 at 10:57:42 UTC, RazvanN wrote: This has already been fixed, you just need to use -preview=fixImmutableConv. This was put behind a preview flag as it introduces a breaking change. I just tried that flag on run.dlang.org, and although it fixes the case I posted earlier, it doesn't fix this one: ```d string test(const(ubyte)[] arr) { import std.string; return arr.assumeUTF; } ``` Shouldn't this be rejected as well? Indeed, that should be rejected as well, otherwise you can modify immutable table. This code currently happily compiles: ```d string test(const(ubyte)[] arr) { import std.string; return arr.assumeUTF; } void main() { import std.stdio; ubyte[] arr = ['a', 'b', 'c']; auto t = test(arr); writeln(t); arr[0] = 'x'; writeln(t); } ``` And prints: ``` abc xbc ``` However, this seems to be a different issue.
Re: std.string.assumeUTF() silently casting mutable to immutable?
On Wednesday, 14 February 2024 at 10:57:42 UTC, RazvanN wrote: This has already been fixed, you just need to use -preview=fixImmutableConv. This was put behind a preview flag as it introduces a breaking change. I just tried that flag on run.dlang.org, and although it fixes the case I posted earlier, it doesn't fix this one: ```d string test(const(ubyte)[] arr) { import std.string; return arr.assumeUTF; } ``` Shouldn't this be rejected as well?
Re: std.string.assumeUTF() silently casting mutable to immutable?
On Tuesday, 13 February 2024 at 14:05:03 UTC, Johan wrote: On Tuesday, 13 February 2024 at 08:10:20 UTC, Jonathan M Davis wrote: So, there's definitely a bug here, but it's a dmd bug. Its checks for whether it can safely change the constness of the return type apparently aren't sophisticated enough to catch this case. This is a pretty severe bug. Thanks, gents. Reported on the tracker: https://issues.dlang.org/show_bug.cgi?id=24394
Re: std.string.assumeUTF() silently casting mutable to immutable?
On Tuesday, 13 February 2024 at 08:10:20 UTC, Jonathan M Davis wrote: So, there's definitely a bug here, but it's a dmd bug. Its checks for whether it can safely change the constness of the return type apparently aren't sophisticated enough to catch this case. This is a pretty severe bug. Some test cases: https://d.godbolt.org/z/K1fjdj76M ```d ubyte[] pure_ubyte(ubyte[] arr) pure @safe; ubyte[] pure_void(void[] arr) pure @safe; ubyte[] pure_int(int[] arr) pure @safe; int[] pure_ubyte_to_int(ubyte[] arr) pure @safe; // All cases below should not compile, yet some do. immutable(ubyte)[] test(ubyte[] arr) @safe { // return with_ubyte(arr); // ERROR: OK return pure_void(arr); // No error: NOK! } immutable(ubyte)[] test(int[] arr) @safe { return pure_int(arr); // No error: NOK! } immutable(int)[] test2(ubyte[] arr) @safe { return pure_ubyte_to_int(arr); // No error: NOK! } ``` -Johan
Re: std.string.assumeUTF() silently casting mutable to immutable?
On Tuesday, February 13, 2024 12:40:57 AM MST Forest via Digitalmars-d-learn wrote: > I may have found a bug in assumeUTF(), but being new to D, I'm > not sure. > > The description: > > Assume the given array of integers arr is a well-formed UTF > > string and return it typed as a UTF string. > > ubyte becomes char, ushort becomes wchar and uint becomes > > dchar. Type qualifiers are preserved. > > The declaration: > > ```d > auto assumeUTF(T)(T[] arr) > if (staticIndexOf!(immutable T, immutable ubyte, immutable > ushort, immutable uint) != -1) > ``` > > Shouldn't that precondition's `immutable T` be simply `T`? > > As it stands, I can do this with no complaints from the > compiler... > > ```d > string test(ubyte[] arr) > { > import std.string; > return arr.assumeUTF; > } > > ``` > > ...and accidentally end up with a "string" pointing at mutable > data. > > Am I missing something? It's not a bug in assumeUTF. if you changed your code to string test(ubyte[] arr) { import std.string; pragma(msg, typeof(arr.assumeUTF)); return arr.assumeUTF; } then the compiler will output char[] because assumeUTF retains the type qualifier of the original type (as its documentation explains). Rather, it looks like the problem here is that dmd will implictly change the constness of a return value when it thinks that it can do so to make the code work. Essentially, that means that the function has to be pure and that the return value can't have come from any of the function's arguments. And at a glance, that would be true here, because no char[] was passed into assumeUTF. However, casting from ubyte[] to char[] is @safe, so dmd should be taking that possibility into account, and it's apparently not. So, there's definitely a bug here, but it's a dmd bug. Its checks for whether it can safely change the constness of the return type apparently aren't sophisticated enough to catch this case. - Jonathan M Davis
std.string.assumeUTF() silently casting mutable to immutable?
I may have found a bug in assumeUTF(), but being new to D, I'm not sure. The description: Assume the given array of integers arr is a well-formed UTF string and return it typed as a UTF string. ubyte becomes char, ushort becomes wchar and uint becomes dchar. Type qualifiers are preserved. The declaration: ```d auto assumeUTF(T)(T[] arr) if (staticIndexOf!(immutable T, immutable ubyte, immutable ushort, immutable uint) != -1) ``` Shouldn't that precondition's `immutable T` be simply `T`? As it stands, I can do this with no complaints from the compiler... ```d string test(ubyte[] arr) { import std.string; return arr.assumeUTF; } ``` ...and accidentally end up with a "string" pointing at mutable data. Am I missing something?
Re: On assigning const to immutable
On Thursday, 13 July 2023 at 11:55:17 UTC, Ki Rill wrote: Why does the first example `class A` work, but the second one with `class B` does not? ```D class A { immutable int a; this(in int a) { this.a = a; } } class B { immutable int[] b; this(in int[] b) { this.b = b; } } void main() { auto a = new A(1); auto b = new B([1, 2]); } ``` It implicitly converts `const` to `immutable`, but fails to do the same with an array. Is it intentional? Why? Yep: `immutable` is data that *cannot ever* change. If you could convert `int[]` to `immutable int[]`, you could do this: ``` int[] a = [2]; immutable(int)[] b = a; auto first = b[0]; a[0] = 3; auto second = b[0]; // Wait, this fails? I thought `b` was immutable. assert(first == second); ``` Use `array.idup` (immutable dup) to turn `int[]` into `immutable int[]`. And you can convert `int` to `immutable int` because it's a "value copy": you cannot mutate the immutable variable through an assignment to the original. Generally speaking, only the constness of referenced data matters for assignment. That's why you can assign `string` to `immutable string`, because `string` is `immutable(char)[]`: the referenced data is immutable in both cases.
Re: On assigning const to immutable
On Thursday, 13 July 2023 at 11:55:17 UTC, Ki Rill wrote: Why does the first example `class A` work, but the second one with `class B` does not? ```D class A { immutable int a; this(in int a) { this.a = a; } } class B { immutable int[] b; this(in int[] b) { this.b = b; } } void main() { auto a = new A(1); auto b = new B([1, 2]); } ``` It implicitly converts `const` to `immutable`, but fails to do the same with an array. Is it intentional? Why? Oh, is it because the first one is passed by value, but the second one is a reference?
On assigning const to immutable
Why does the first example `class A` work, but the second one with `class B` does not? ```D class A { immutable int a; this(in int a) { this.a = a; } } class B { immutable int[] b; this(in int[] b) { this.b = b; } } void main() { auto a = new A(1); auto b = new B([1, 2]); } ``` It implicitly converts `const` to `immutable`, but fails to do the same with an array. Is it intentional? Why?
Para que sirve o que son las variables "immutable"?
Tengo una duda y es que en la documentacion de std.json hay un ejemplo de codigo que por identidicador ponen "immutable", nose para que sirve o de que trata. :( alguien me diga porfis :)
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On Wednesday, 31 May 2023 at 09:14:49 UTC, Dom DiSc wrote: On Wednesday, 31 May 2023 at 03:29:33 UTC, Cecil Ward wrote: I have to admit that I don’t really understand immutable. I have an idea that it could mean that an object has an address in ROM, so its value will never change. Maybe const doesn’t give you such a strong guarantee, disallows ‘you’ from modifying it but others might do so, but who knows. There are two perspectives: that of the value handed to a function and that of the function taking the value. "immutable" (or "mutable") is a property of the value, "const" is a property of the function. If the function can work with mutable values, but in fact doesn't mutate them itself, it could also work with immutable values. The fact that others could modify your "const" value doesn't matter for immutable values, because they of course can't be modified by others. For the function it doesn't matter, because it only guarantees not to modify it itself, don't care about what other can or can't do. Without a guarantee as strong as the first idea I can’t really understand how const can work properly. "You treat it as const so do not modify it, but it might not be eternally fixed and unchanging" that doesn’t seem to have enough value to me. Why? What guarantee are you missing? Your function can work with mutable data, so you don't care if it can be modified also by others. Now it happens that you doesn't modify the data. So why shouldn't you be able to work on data that guarantees that it also will not be changed by others? You don't care for such modification anyway. The meaning of "immutable" is: I cannot be modified. Not by you and not by anybody else. It's a property of a value. The meaning of "mutable" is: I can be modified by anybody. Work with me only if that is ok for you. It's a property of a value. The meaning of "const" is: I don't care if others modify the data or not, I won't modify it myself. It's a property of a function. Dom, you explain it well. I’m just too stupid. Literally, as I’m on strong pain drugs all the time, so things are a bit fuzzy. As a professional C programmer for some years, I understood the word const and used it all the time, as much as possible at every opportunity, so I had some expectations. But the errors I’m getting don’t fit in with the previous understanding I had with the familiar ‘const’ keyword and make me think there must be something else going on. So I will need to capture an example in the wild, cage it and bring it in for scientific examination. I can’t ask for an explanation of things going on that you can’t inspect for yourself, obviously. And I don’t understand what the problem is with using in as much as possible yet having to remove it sometimes when something immutable is in use.
Re: static immutable that has no initialiser - should this raise an error?
On Tuesday, 30 May 2023 at 04:11:00 UTC, Cecil Ward wrote: static immutable T foo; T bar() { return foo; } Should we get an error from the D compiler here as the initialiser has been forgotten? What do you think ? No. There are no un-initialized values in D. It gets its default value, if no explicit value is given. That's intended, not an error. If you need an un-initialized value, set it =void. Of course, for immutable values that makes no sense at all. But I think, this already gives you an error.
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On Wednesday, 31 May 2023 at 03:29:33 UTC, Cecil Ward wrote: I have to admit that I don’t really understand immutable. I have an idea that it could mean that an object has an address in ROM, so its value will never change. Maybe const doesn’t give you such a strong guarantee, disallows ‘you’ from modifying it but others might do so, but who knows. There are two perspectives: that of the value handed to a function and that of the function taking the value. "immutable" (or "mutable") is a property of the value, "const" is a property of the function. If the function can work with mutable values, but in fact doesn't mutate them itself, it could also work with immutable values. The fact that others could modify your "const" value doesn't matter for immutable values, because they of course can't be modified by others. For the function it doesn't matter, because it only guarantees not to modify it itself, don't care about what other can or can't do. Without a guarantee as strong as the first idea I can’t really understand how const can work properly. "You treat it as const so do not modify it, but it might not be eternally fixed and unchanging" that doesn’t seem to have enough value to me. Why? What guarantee are you missing? Your function can work with mutable data, so you don't care if it can be modified also by others. Now it happens that you doesn't modify the data. So why shouldn't you be able to work on data that guarantees that it also will not be changed by others? You don't care for such modification anyway. The meaning of "immutable" is: I cannot be modified. Not by you and not by anybody else. It's a property of a value. The meaning of "mutable" is: I can be modified by anybody. Work with me only if that is ok for you. It's a property of a value. The meaning of "const" is: I don't care if others modify the data or not, I won't modify it myself. It's a property of a function.
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On Wednesday, 31 May 2023 at 03:23:01 UTC, Cecil Ward wrote: On Tuesday, 30 May 2023 at 04:15:22 UTC, Ali Çehreli wrote: On 5/29/23 19:57, Cecil Ward wrote: > I wish to have one routine > that can be called with either immutable or (possibly) mutable argument > values. 'const' should take both immutable and mutable. Can you show your case with a short example? > Could I make the one routine into a template? That could work but give 'in' parameters a try: https://dlang.org/spec/function.html#in-params Ali T2 foo( in T1 x ) { return bar( x ) }; It was with something vaguely like the above that I had to remove the in (templatised generic function possibly) in order to get it to compile with GDC or LDC on godbolt.org (or d.godbolt.org) latest versions available. -O3 -release/-frelease -march=native/-mcpu-native I have to admit that I don’t really understand immutable. I have an idea that it could mean that an object has an address in ROM, so its value will never change. Maybe const doesn’t give you such a strong guarantee, disallows ‘you’ from modifying it but others might do so, but who knows. Without a guarantee as strong as the first idea I can’t really understand how const can work properly. "You treat it as const so do not modify it, but it might not be eternally fixed and unchanging" that doesn’t seem to have enough value to me. But maybe I’ve got the whole thing wrong. In an architecture where you have strongly typed (tagged ? segmented?) different kinds of addresses, I can see why you might be getting type mismatch errors when passing addresses around.
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On Tuesday, 30 May 2023 at 04:15:22 UTC, Ali Çehreli wrote: On 5/29/23 19:57, Cecil Ward wrote: > I wish to have one routine > that can be called with either immutable or (possibly) mutable argument > values. 'const' should take both immutable and mutable. Can you show your case with a short example? > Could I make the one routine into a template? That could work but give 'in' parameters a try: https://dlang.org/spec/function.html#in-params Ali T2 foo( in T1 x ) { return bar( x ) }; It was with something vaguely like the above that I had to remove the in (templatised generic function possibly) in order to get it to compile with GDC or LDC on godbolt.org (or d.godbolt.org) latest versions available. -O3 -release/-frelease -march=native/-mcpu-native
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On 5/29/23 10:57 PM, Cecil Ward wrote: I have often come into difficulties where I wish to have one routine that can be called with either immutable or (possibly) mutable argument values. The argument(s) in question are in, readonly, passed by value or passed by const reference. Anyway, no one is trying to write to the items passed in as args, no badness attempted. In cases where const doesn't suffice, inout might. inout has a feature that it can be used in cases where a const nested behind multiple references would not work. -Steve
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On 5/29/23 19:57, Cecil Ward wrote: > I wish to have one routine > that can be called with either immutable or (possibly) mutable argument > values. 'const' should take both immutable and mutable. Can you show your case with a short example? > Could I make the one routine into a template? That could work but give 'in' parameters a try: https://dlang.org/spec/function.html#in-params Ali
Re: Code duplication where you wish to have a routine called with either immutable or mutable arguments
On Tuesday, 30 May 2023 at 02:57:52 UTC, Cecil Ward wrote: I have often come into difficulties where I wish to have one routine that can be called with either immutable or (possibly) mutable argument values. The argument(s) in question are in, readonly, passed by value or passed by const reference. Anyway, no one is trying to write to the items passed in as args, no badness attempted. When I call the routine from one place with an argument that is immutable and then from another that is not, or it could be const as well, or not, that’s when I get all kinds of type mismatch errors. And I certainly don’t want to pour cast-like type conversion operations over all the place at these problem occurrences. it’s surely asking for problems. Normally you would use const for this, since a const parameter can accept either a mutable or an immutable argument. If const isn't working for you, then there's probably something else going on that you haven't mentioned. Can you post an example of the kind of code that gives you these errors?
Code duplication where you wish to have a routine called with either immutable or mutable arguments
I have often come into difficulties where I wish to have one routine that can be called with either immutable or (possibly) mutable argument values. The argument(s) in question are in, readonly, passed by value or passed by const reference. Anyway, no one is trying to write to the items passed in as args, no badness attempted. When I call the routine from one place with an argument that is immutable and then from another that is not, or it could be const as well, or not, that’s when I get all kinds of type mismatch errors. And I certainly don’t want to pour cast-like type conversion operations over all the place at these problem occurrences. it’s surely asking for problems. Could I make the one routine into a template? Even if I did, that would perhaps create additionally problems, since even if it all worked and instantiated either immutable or mutable forms of the routine, what happens if I have two args and they have a mixture of immutable and no immutable args ? So nargs * 2 ( or more) possibilities? I’m out of my depth here.
Re: Transform static immutable string array to tuple.
Awesome, Thank both of you! ``` enum a = ["A", "B"]; writeln(a); writeln(aliasSeqOf!a); writeln([aliasSeqOf!a]); ```
Re: Transform static immutable string array to tuple.
On Sun, Feb 19, 2023 at 11:08:34AM +, realhet via Digitalmars-d-learn wrote: > Hello, > > Is there a better way to transform a string array to a tuple or to an > AliasSeq? > > ``` > mixin(customSyntaxPrefixes.format!`tuple(%(%s,%))`) > ``` > > I'd like to use this as variable length arguments passed to the > startsWith() std function (as multiple needles). In situations like this it's often better to define your dats as an AliasSeq to begin with, since it's easier to covert that to an array than the other way round. T -- I don't trust computers, I've spent too long programming to think that they can get anything right. -- James Miller
Re: Transform static immutable string array to tuple.
On Sunday, 19 February 2023 at 11:08:34 UTC, realhet wrote: Is there a better way to transform a string array to a tuple or to an AliasSeq? ``` mixin(customSyntaxPrefixes.format!`tuple(%(%s,%))`) ``` https://dlang.org/phobos/std_meta.html#aliasSeqOf
Transform static immutable string array to tuple.
Hello, Is there a better way to transform a string array to a tuple or to an AliasSeq? ``` mixin(customSyntaxPrefixes.format!`tuple(%(%s,%))`) ``` I'd like to use this as variable length arguments passed to the startsWith() std function (as multiple needles).
Re: Error: cannot use non-constant CTFE pointer in an initializer `cast(immutable(char)*)TEST`
On Saturday, 11 June 2022 at 11:51:43 UTC, Tejas wrote: On Saturday, 11 June 2022 at 10:07:46 UTC, test123 wrote: how to work this around. ```d __gshared const TEST = import(`onlineapp.d`); extern(C) void main(){ __gshared bin_ptr = TEST.ptr; } ``` ```sh dmd2 -betterC -J. onlineapp.d onlineapp.d(3): Error: cannot use non-constant CTFE pointer in an initializer `cast(immutable(char)*)TEST` ``` Maybe try putting `__gshared bin_ptr = TEST.ptr;` inside a `shared static this(){}`? I mean, write it like: ```d __gshared bin_ptr; shared static this() { bin_ptr = TEST.ptr; } __gshared const TEST = import(`onlineapp.d`); extern(C) void main() { } ``` But `bin_ptr` becomes a global in this case :( Oh wait, `-betterC` is being used ;( hmm... Use [pragma(crt_constructor)](https://dlang.org/spec/pragma.html#crtctor) instead ```d __gshared immutable(char)* bin_ptr; pragma(crt_constructor) void init_ptr() { bin_ptr = TEST.ptr; } __gshared TEST = import("this.d"); extern(C) void main() { } ``` ```sh (base) [tejasgarhewal@fedora D]$ ldc2 -J. -betterC this.d (base) [tejasgarhewal@fedora D]$ ./this ```
Re: Error: cannot use non-constant CTFE pointer in an initializer `cast(immutable(char)*)TEST`
On Saturday, 11 June 2022 at 10:07:46 UTC, test123 wrote: how to work this around. ```d __gshared const TEST = import(`onlineapp.d`); extern(C) void main(){ __gshared bin_ptr = TEST.ptr; } ``` ```sh dmd2 -betterC -J. onlineapp.d onlineapp.d(3): Error: cannot use non-constant CTFE pointer in an initializer `cast(immutable(char)*)TEST` ``` Maybe try putting `__gshared bin_ptr = TEST.ptr;` inside a `shared static this(){}`? I mean, write it like: ```d __gshared bin_ptr; shared static this() { bin_ptr = TEST.ptr; } __gshared const TEST = import(`onlineapp.d`); extern(C) void main() { } ``` But `bin_ptr` becomes a global in this case :(
Error: cannot use non-constant CTFE pointer in an initializer `cast(immutable(char)*)TEST`
how to work this around. ```d __gshared const TEST = import(`onlineapp.d`); extern(C) void main(){ __gshared bin_ptr = TEST.ptr; } ``` ```sh dmd2 -betterC -J. onlineapp.d onlineapp.d(3): Error: cannot use non-constant CTFE pointer in an initializer `cast(immutable(char)*)TEST` ```
Re: Why do immutable variables need reference counting?
On Mon, Apr 18, 2022 at 12:55:26PM +, wjoe via Digitalmars-d-learn wrote: > On Sunday, 17 April 2022 at 14:14:37 UTC, H. S. Teoh wrote: > > Not entirely true. See paragraph 3 in: > > > > https://dlang.org/spec/unittest.html > > > > and 10.24.11.3 in: > > > > https://dlang.org/spec/expression.html#assert_expressions [...] > Thanks. Either I missed that the last time I checked or it wasn't > there ;) > But the interesting question is: does the compiler go out of its way > to make that happen or is it just paragraphs written in the spec? My guess: the spec was amended after the fact. :-D T -- Those who don't understand D are condemned to reinvent it, poorly. -- Daniel N
Re: Why do immutable variables need reference counting?
On Sunday, 17 April 2022 at 14:14:37 UTC, H. S. Teoh wrote: Not entirely true. See paragraph 3 in: https://dlang.org/spec/unittest.html and 10.24.11.3 in: https://dlang.org/spec/expression.html#assert_expressions T Thanks. Either I missed that the last time I checked or it wasn't there ;) But the interesting question is: does the compiler go out of its way to make that happen or is it just paragraphs written in the spec?
Re: Why do immutable variables need reference counting?
On 17.04.22 15:27, H. S. Teoh wrote: On Sun, Apr 17, 2022 at 01:06:36PM +, wjoe via Digitalmars-d-learn wrote: [...] On the matter of undefined behavior. Technically a program is in undefined behavior land after throwing an error, thus every unittest that continues after assertThrown is therefore nonsense code, is it not ? The spec explicitly makes an exception(!) in this case. See 10.24.11.3: https://dlang.org/spec/expression.html#assert_expressions as well as 27.3: https://dlang.org/spec/unittest.html But the failing assert is not part of the unittest directly. It's in a function that is called from the unittest. It takes a generous interpretation of the spec to include such asserts in the exception. Letting failing asserts cause UB is a mistake anyway. It stems from a misguided desire to re-use asserts for optimization, when nobody actually uses them that way.
Re: Why do immutable variables need reference counting?
On Sun, Apr 17, 2022 at 04:09:12PM +0200, ag0aep6g via Digitalmars-d-learn wrote: [...] > Failing asserts are a messy part of the language. They are supposed to > be: > > 1) not catchable, because they indicate a bug in the program; > 2) catchable in order to be testable; > 3) assumed impossible for optimization purposes. > > Those goals are at odds with each other, and I don't think the spec > manages to consolidate them. Not entirely true. See paragraph 3 in: https://dlang.org/spec/unittest.html and 10.24.11.3 in: https://dlang.org/spec/expression.html#assert_expressions T -- Latin's a dead language, as dead as can be; it killed off all the Romans, and now it's killing me! -- Schoolboy
Re: Why do immutable variables need reference counting?
On 17.04.22 15:06, wjoe wrote: On the matter of undefined behavior. Technically a program is in undefined behavior land after throwing an error, thus every unittest that continues after assertThrown is therefore nonsense code, is it not ? Yes. Failing asserts are a messy part of the language. They are supposed to be: 1) not catchable, because they indicate a bug in the program; 2) catchable in order to be testable; 3) assumed impossible for optimization purposes. Those goals are at odds with each other, and I don't think the spec manages to consolidate them. But mutating `immutable` data is not messy. It's simply not allowed.
Re: Why do immutable variables need reference counting?
On Sun, Apr 17, 2022 at 01:06:36PM +, wjoe via Digitalmars-d-learn wrote: [...] > On the matter of undefined behavior. Technically a program is in > undefined behavior land after throwing an error, thus every unittest > that continues after assertThrown is therefore nonsense code, is it > not ? The spec explicitly makes an exception(!) in this case. See 10.24.11.3: https://dlang.org/spec/expression.html#assert_expressions as well as 27.3: https://dlang.org/spec/unittest.html T -- An elephant: A mouse built to government specifications. -- Robert Heinlein
Re: Why do immutable variables need reference counting?
On Thursday, 14 April 2022 at 12:10:04 UTC, ag0aep6g wrote: On 14.04.22 13:42, wjoe wrote: Undefined behavior yes, but regardless the example proves it can be done in @system code. A few versions ago, possibly due to a bug or regression, the compiler didn't complain in @safe code either. Of course you are correct academically. However, since it's possible, I'd wager my last hat that code like this is out in the wild. No, it cannot be done in @system code. The example only proves that you can write nonsense code that has no defined meaning. Note that the nonsense you wrote behaves as you describe only in Windows. Elsewhere, you get a segfault. But it doesn't matter how the executable actually behaves. You cannot cite the result of undefined behavior when arguing language semantics. Well I'm not using Windows so I wouldn't know but I compiled an ran that program on Linux and it didn't segfault. If it had I wouldn't have included that part in my reply. On the matter of undefined behavior. Technically a program is in undefined behavior land after throwing an error, thus every unittest that continues after assertThrown is therefore nonsense code, is it not ?
Re: Why do immutable variables need reference counting?
On 14.04.22 13:42, wjoe wrote: Undefined behavior yes, but regardless the example proves it can be done in @system code. A few versions ago, possibly due to a bug or regression, the compiler didn't complain in @safe code either. Of course you are correct academically. However, since it's possible, I'd wager my last hat that code like this is out in the wild. No, it cannot be done in @system code. The example only proves that you can write nonsense code that has no defined meaning. Note that the nonsense you wrote behaves as you describe only in Windows. Elsewhere, you get a segfault. But it doesn't matter how the executable actually behaves. You cannot cite the result of undefined behavior when arguing language semantics.
Re: Why do immutable variables need reference counting?
On Tuesday, 12 April 2022 at 23:23:59 UTC, Ali Çehreli wrote: [...] Looking at this from a technical perspective - everything you say is true - and thanks for clearing up some of my confusion in that department. Looking at this from a natural language (English) perspective - words prompt ideas and expectations. If these expectations aren't met confusion and misunderstanding happens (and that's not just true for software development). Although, perhaps, that's only a problem for (some) non native English speakers ? The fact remains that there's confusion and misunderstandings regarding const, immutable and const 2.
Re: Why do immutable variables need reference counting?
On Tuesday, 12 April 2022 at 22:23:18 UTC, ag0aep6g wrote: On Tuesday, 12 April 2022 at 19:54:13 UTC, wjoe wrote: Especially since it's only a promise and the compiler accepts this: void foo (const(char)[] arr) { cast(char[])arr[0..3] = "baz"; } string bar = "123"; foo(bar); assert(bar=="baz"); But I could cast away const and modify the string bar. No, you could not. You're relying on undefined behavior there. Just because the compiler accepts something, doesn't mean it's ok. If you want to be guarded against wandering into undefined territory, that's what @safe does. With @safe, the cast doesn't compile. Undefined behavior yes, but regardless the example proves it can be done in @system code. A few versions ago, possibly due to a bug or regression, the compiler didn't complain in @safe code either. Of course you are correct academically. However, since it's possible, I'd wager my last hat that code like this is out in the wild.
Re: Why do immutable variables need reference counting?
On Wed, Apr 13, 2022 at 08:39:17AM -0700, Ali Çehreli via Digitalmars-d-learn wrote: > On 4/12/22 21:34, Salih Dincer wrote: > > > I tried the following and I didn't understand one thing: Why is > > there no need to use dup when slicing? [...] Because of two things: (1) there is a GC, and (2) characters in a string are immutable. Without (1), you will end up with either a memory leak or a dangling pointer; without (2), your slice may randomly mutate when you don't expect it to. T -- May you live all the days of your life. -- Jonathan Swift
Re: Why do immutable variables need reference counting?
On 4/12/22 21:34, Salih Dincer wrote: > I tried the following and I didn't understand one thing: Why is there no > need to use dup when slicing? I don't think I understand you fully. > ```d > struct S(T) { > T fileName; > >this(T fileName) { > this.fileName = fileName; > report(); >} > >~this() { report(); } > >void report(string func = __FUNCTION__) { > import std.stdio; > writefln!"%s\nworking with %s"(func, fileName); >} > } > alias T1 = const(char)[]; > alias T2 = const char[]; > alias T3 = const(char[]); // T3 == T2 > alias T4 = immutable char[]; // Not compiling! > > > void main() { >auto fileName = "foo.txt".dup; In every test case that 'fileName' is char[]. >auto s = S!T1(fileName); >fileName[0..3] = "bar"; For that reason, that expression will always succeed. And that's the point I tried to make: Without immutable, struct S *must* make a copy in order to guarantee that its member will remain the same. > }/* Results: [...] >* >*T4 >* >Error: slice `fileName[0..3]` is not mutable Your code was different because I get an error that I expect: The following line fails. auto s = S!T4(fileName); cannot pass argument `fileName` of type `char[]` to parameter `immutable(string) fileName` Exactly! struct S wants immutable (to guarantee that nobody will mutate it) but 'fileName' in main is not immutable. You can make it pass in the T4 case a) by making an immutable copy with .idup: auto s = S!T4(fileName.idup); b) by converting 'fileName' to immutable without a copy: import std.exception; auto s = S!T4(fileName.assumeUnique); // But then, the following will fail. Awesome! // fileName[0..3] = "bar"; I am amazed everyday how great D is; such a fun engineering tool! Ali
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 21:48:56 UTC, Ali Çehreli wrote: On 4/11/22 08:02, Paul Backus wrote: > any pointers or references To add, Salih and I were in an earlier discussion where that concept appeared as "indirections." Ali I tried the following and I didn't understand one thing: Why is there no need to use dup when slicing? ```d struct S(T) { T fileName; this(T fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { import std.stdio; writefln!"%s\nworking with %s"(func, fileName); } } alias T1 = const(char)[]; alias T2 = const char[]; alias T3 = const(char[]); // T3 == T2 alias T4 = immutable char[]; // Not compiling! void main() { auto fileName = "foo.txt".dup; auto s = S!T1(fileName); fileName[0..3] = "bar"; }/* Results: * *T1: * source.S!(const(char)[]).S.this working with foo.txt. source.S!(const(char)[]).S.~this working with bar.txt. * *T2: * source.S!(const(char[])).S.this working with foo.txt source.S!(const(char[])).S.~this working with bar.txt * *T3: * source.S!(const(char[])).S.this working with foo.txt source.S!(const(char[])).S.~this working with bar.txt * *T4 * Error: slice `fileName[0..3]` is not mutable */ ``` SDB@79
Re: Why do immutable variables need reference counting?
On 4/12/22 12:54, wjoe wrote: > I.e. immutable is constant data which is created at compile time - like > laws of physics, For completeness, we already have 'enum' and 'static const' for that. > should get a better name - maybe 'in' and get rid of const. Yes! 'in' for parameters! :D >> import std.stdio; >> import std.format; >> >> struct S { >> const(char)[] fileName; >> >> this(const(char)[] fileName) { >> this.fileName = fileName; >> report(); >> } >> >> ~this() { >> report(); >> } >> >> void report(string func = __FUNCTION__) { >> writefln!"%s working with %s."(func, fileName); >> } >> } >> >> void main() { >> char[] fileName = "foo.txt".dup; >> auto s = S(fileName); >> fileName[0..3] = "bar"; >> } >> >> >> If fileName were immutable, then the owner would not be able to mutate >> anyway, so the struct could get away without copying the file name. >> >> Ali > > I presume you refer to fileName in main() ? No, I meant the member 'fileName'. If the member is 'const' (as in C++ and as in the example above), you have to take a copy because the caller may mutate. (The example is demonstrating the case where the struct assumes data won't changed and gets surprised by the mutation.) > And if yes, if it were > const, it couldn't be mutated either, You started as assuming 'fileName' in main, so, I will continue with that. If the struct had 'const fileName' and pleaded that the caller also define a 'const' variable, that would be wishful thinking. The struct cannot trust that all callers will obey that plea. That kind of trust (more like guarantee) comes with 'immutable' and this is a good example of how immutable is different from const. (immutable does not exist in some languages like C++.) > so isn't immutable and const sort > of synonymous in that case or am I missing your point? If on the other hand, the member were immutable, the caller would have to provide immutable data and the struct could rely on the fact that the data was immutable forever. No need for a copy... Ali
Re: Why do immutable variables need reference counting?
On Tuesday, 12 April 2022 at 19:54:13 UTC, wjoe wrote: Especially since it's only a promise and the compiler accepts this: void foo (const(char)[] arr) { cast(char[])arr[0..3] = "baz"; } string bar = "123"; foo(bar); assert(bar=="baz"); But I could cast away const and modify the string bar. No, you could not. You're relying on undefined behavior there. Just because the compiler accepts something, doesn't mean it's ok. If you want to be guarded against wandering into undefined territory, that's what @safe does. With @safe, the cast doesn't compile.
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote: On 4/11/22 05:57, wjoe wrote: > And because the data could be > in ROM any modification is an error. Fully agreed. However, how could I initialize such an object then? (You may have meant a read-only memory page instead of ROM.) I was thinking during compile time. By initializing a variable with immutable data or a pointer that points to an address e.G. an EEPROM. Even 'const' cause confusions because it's used in at least two different ways (even e.g. in C++): 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all! In general, I guess, it's a bad idea to reuse the same word for 2 or more distinctly different ideas. Maybe the const keyword in 1) should have a better name. Especially since it's only a promise and the compiler accepts this: void foo (const(char)[] arr) { cast(char[])arr[0..3] = "baz"; } string bar = "123"; foo(bar); assert(bar=="baz"); But I could cast away const and modify the string bar. So with that said I don't agree with you when you say immutability should be const's domain because const covers item 1 above as well, where there is no immutability of data whatsoever. The data may be perfectly mutable or immutable, where my access will be readonly. When I said immutability should be the domain of const I am referring only to 2). I.e. immutable is constant data which is created at compile time - like laws of physics, const as in 2) is constant data which is created at run time - like man made laws, and 1) should get a better name - maybe 'in' and get rid of const. And to be able to use immutable anywhere other than where immutable is explicitly specified, a copy is necessary. I know it's not as simple as that. But one can dream, right? :) producer from mutating it further. Example: import std.stdio; import std.format; struct S { const(char)[] fileName; this(const(char)[] fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { writefln!"%s working with %s."(func, fileName); } } void main() { char[] fileName = "foo.txt".dup; auto s = S(fileName); fileName[0..3] = "bar"; } If fileName were immutable, then the owner would not be able to mutate anyway, so the struct could get away without copying the file name. Ali I presume you refer to fileName in main() ? And if yes, if it were const, it couldn't be mutated either, so isn't immutable and const sort of synonymous in that case or am I missing your point?
Re: Why do immutable variables need reference counting?
On 4/12/22 03:28, Dom DiSc wrote: > On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote: >> 1) I will not mutate data through this reference. For example, a >> parameter that is pointer to const achieves that: >> >> void foo (const(int)[] arr); >> >> 2) This variable is const: >> >> const i = 42; >> >> Well, there is the confusion: There is no "reference" in the second >> case at all! > I think this second case should not be allowed. Use > > immutable i = 42; > > instead. The meaning is identical, Only if there are no indirections as in 'int'. If I use a struct struct S { const(char[]) arr; } void foo(const(char[]) arr) { immutable s = immutable(S)(arr); // <-- ERROR: // Error: cannot implicitly convert expression `arr` of type `const(char[])` to `immutable(string)` } void main() { int[] arr; foo(arr); } The reason is a property of immutable that I like to describe as "selective" (or "demanding"). The struct variable cannot be immutable because it would be demanding that its constructor argument be immutable, which cannot be because foo's 'const' parameter hides that information even when it were immutable e.g. in main(). > but we could remove the burden of two > different meanings from const if it is not allowed. const should only be > allowed in function declarations. A variable must be declared either > mutable or immutable. It's only functions that may guarantee not to > modify a parameter or the objects they belong to, and so are allowed to > work on both mutable and immutable objects. I understand but the guarantee of immutable seems to leave no other choice. Ali
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote: 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all! I think this second case should not be allowed. Use immutable i = 42; instead. The meaning is identical, but we could remove the burden of two different meanings from const if it is not allowed. const should only be allowed in function declarations. A variable must be declared either mutable or immutable. It's only functions that may guarantee not to modify a parameter or the objects they belong to, and so are allowed to work on both mutable and immutable objects.
Re: Why do immutable variables need reference counting?
On 4/11/22 05:57, wjoe wrote: > To my understanding immutable data should reside in a separate data > segment which itself could reside in ROM. We are getting into implementation details which a programming language acts as not to care (but has to do especially when it's a system programming language like D. :) ). > So when the variable, or 'pointer' to the data, goes out of scope just > the 'pointer' is gone, the actual data unreachable, but still there. I think it translates to the destructor never being executed. Otherwise, nobody would even know whether the memory location is reused for other pursposes later on. > Due to its immutable nature immutable data can't be changed and this, to > my understanding, includes deallocation. D one language where object lifetime is deliberately separate from memory allocation. > And because the data could be > in ROM any modification is an error. Fully agreed. However, how could I initialize such an object then? (You may have meant a read-only memory page instead of ROM.) > immutable data on the fly doesn't make sense to me - that should be > const's domain. Even 'const' cause confusions because it's used in at least two different ways (even e.g. in C++): 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that: void foo (const(int)[] arr); 2) This variable is const: const i = 42; Well, there is the confusion: There is no "reference" in the second case at all! I don't agree with you when you say immutability should be const's domain because const covers item 1 above as well, where there is no immutability of data whatsoever. The data may be perfectly mutable or immutable, where my access will be readonly. Perhaps you are saying the same thing but enters D's immutable: The data is immutable. immutable data on the fly can be very useful because e.g. it makes multithreaded programming trivial in some cases by removing the need for locking. > Strings, I know. But the way things are, I hardly see a > difference between immutable and const. Imagine a File struct that holds on to a file name. In C++, we would have to make a copy of the constructor argument for two reasons: 1) Lifetime of the object might be short. This is not a problem in D because of the GC. 2) The data might change after I start holding on to it through a reference. This is not a problem *only if* data were immutable because my const parameter cannot preclude the producer from mutating it further. Example: import std.stdio; import std.format; struct S { const(char)[] fileName; this(const(char)[] fileName) { this.fileName = fileName; report(); } ~this() { report(); } void report(string func = __FUNCTION__) { writefln!"%s working with %s."(func, fileName); } } void main() { char[] fileName = "foo.txt".dup; auto s = S(fileName); fileName[0..3] = "bar"; } The output shows that the file name changed between construction and destruction: deneme.S.this working with foo.txt. deneme.S.~this working with bar.txt. If fileName were immutable, then the owner would not be able to mutate anyway, so the struct could get away without copying the file name. Ali
Re: Why do immutable variables need reference counting?
On 4/11/22 08:02, Paul Backus wrote: > any pointers or references To add, Salih and I were in an earlier discussion where that concept appeared as "indirections." Ali
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 12:12:39 UTC, Salih Dincer wrote: It worked for me in a different way. 1 is (about to be) alive! 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. Hello D! 1 is (already) dead. Because I changed the code like this. ```d struct S { . . . string toString() { return ""; } } //S test(inout S s)/* S test(S s)//*/ { s.i = 2; return s; } void main() { immutable s = S(1); test(s).writeln; "Hello".writefln!"%s D!"; } ``` If the inout is set, it does not allow compilation. Because `S` does not contain any pointers or references, you are allowed to create a mutable *copy* of an `S` from an `immutable` source. That's what happens when you pass it to `test`. The extra destructor calls come from the copies made by `test` and `writeln`. If you add a copy constructor, you can see this happening in the output: ```d struct S { /* ... */ this(ref inout typeof(this) other) inout { this.i = other.i; writeln(i, " was copied."); } } ``` ``` 1 is (about to be) alive! 1 was copied. 2 was copied. 2 is (already) dead. 2 was copied. 2 was copied. 2 was copied. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. Hello D! 1 is (already) dead. ``` 1 constructor call + 5 copies = 6 destructor calls.
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 03:24:11 UTC, Ali Çehreli wrote: On 4/10/22 20:05, norm wrote: > On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermole wrote: > In my mind immutable data > means the data will not change and neither will the result of reading > that data, ever. Yes. > I don't get how you can have thread safety guarantees based on immutable > if reading that data in a thread suddenly becomes undefined behaviour > and could return anything. Yes, it would be a bug to attempt to read data that is not live anymore. > That isn't immutable then. The lifetime of immutable data can start and end. import std.stdio; struct S { int i; this(int i) { this.i = i; writeln(i, " is (about to be) alive!"); } ~this() { writeln(i, " is (already) dead."); } } void main() { foreach (i; 0 .. 3) { immutable s = S(i); } } The output: 0 is (about to be) alive! 0 is (already) dead. 1 is (about to be) alive! 1 is (already) dead. 2 is (about to be) alive! 2 is (already) dead. Module-level immutable and 'static const' would live much longer but they have a lifetime as well. However, today, the destructor cannot be executed on an immutable object, so I remove the qualifiers with cast(), which sohuld be undefined behavior (today?). immutable(S) moduleS; shared static this() { moduleS = S(42); } shared static ~this() { destroy(cast()moduleS); } void main() { } > Once instantiated > immutable data persists for the remainder of the program. That seems to be the misunderstanding. Again, I think module-level 'immutable' and 'static const' data fits that description. > You may not > have access if the variable goes out of scope, but if you do it will > always be there and always return the same value when you read from memory. That description fits D's GC-owned data (including immutables). The lifetime ends when there is no reference to it. Another example is immutable messages passed between threads with std.concurrency: That kind of data clearly originates at run time and the receiving end keeps the data alive as long as it needs. Ali To my understanding immutable data should reside in a separate data segment which itself could reside in ROM. So when the variable, or 'pointer' to the data, goes out of scope just the 'pointer' is gone, the actual data unreachable, but still there. Due to its immutable nature immutable data can't be changed and this, to my understanding, includes deallocation. And because the data could be in ROM any modification is an error. How would you deallocate ROM anyways? Your foreach could be unrolled at compile time, however it could easily be changed to a runtime only loop but this entire concept of creating immutable data on the fly doesn't make sense to me - that should be const's domain. Strings, I know. But the way things are, I hardly see a difference between immutable and const.
Re: Why do immutable variables need reference counting?
On Monday, 11 April 2022 at 03:24:11 UTC, Ali Çehreli wrote: The output: 0 is (about to be) alive! 0 is (already) dead. 1 is (about to be) alive! 1 is (already) dead. 2 is (about to be) alive! 2 is (already) dead. It worked for me in a different way. 1 is (about to be) alive! 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. 2 is (already) dead. Hello D! 1 is (already) dead. Because I changed the code like this. ```d struct S { . . . string toString() { return ""; } } //S test(inout S s)/* S test(S s)//*/ { s.i = 2; return s; } void main() { immutable s = S(1); test(s).writeln; "Hello".writefln!"%s D!"; } ``` If the inout is set, it does not allow compilation. Thanks, SDB79
Re: Why do immutable variables need reference counting?
On Sunday, 10 April 2022 at 23:05:24 UTC, norm wrote: Hi All, I am clearly misunderstanding something fundamental, and probably obvious :D Reading some of the discussions on __metadata I was wondering if someone could explain why a immutable reference counting type is needed. By definition a reference counter cannot be immutable, so what would be the use case that requires it? It cannot really be pure nor safe either because the ref goes out of scope and the allocation is freed. How is this immutable? Thanks, Norm refcounting would require a concept of "tail const" / "tail immutable" so that transitivity of the qualifier does not affect the data used to refcount (basically the field that hold the count) but only the data that **are** refcounted.
Re: Why do immutable variables need reference counting?
On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermole wrote: immutable isn't tied to lifetime semantics. It only says that this memory will never be modified by anyone during its lifetime. Anyway, the real problem is with const. Both mutable and immutable become it automatically. I was thinking about that, often when using const you use it when passing parameters to functions. This is essentially borrowing. The situation is similar with C++ with unique_ptr and shared_ptr. Often C++ interfaces use const* when using pointers and not their smart pointer counterparts, so essentially the ownership remains, while "borrowing" are using raw pointers. A C++ interface only accepts the smart pointers when you want to change ownership. D could use a similar approach, when using pointers/references you shouldn't alter the internal data including reference count. What I would interested in is if D could have move by default depending on type. In this case the RC pointer wrapper could be move by default. Increasing is only done when calling "clone" (similar to Rust). This way RC increases are optimized naturally. What I don't want from Rust is the runtime aliasing check (RefCell) on at all times. I rather go with that the compiler assumes no aliasing but the programmer is responsible for this. You can have runtime aliasing/borrowing check in debug mode but in release build it can be removed. This is similar to bounds checking where you can choose to have it or not.
Re: Why do immutable variables need reference counting?
Storage classes like immutable/const/shared are not tied to any memory management strategy. Nor does it dictate memory lifetime. It only dictates how it can be interacted with when you have a reference to it.
Re: Why do immutable variables need reference counting?
On 4/10/22 20:05, norm wrote: > On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermole wrote: > In my mind immutable data > means the data will not change and neither will the result of reading > that data, ever. Yes. > I don't get how you can have thread safety guarantees based on immutable > if reading that data in a thread suddenly becomes undefined behaviour > and could return anything. Yes, it would be a bug to attempt to read data that is not live anymore. > That isn't immutable then. The lifetime of immutable data can start and end. import std.stdio; struct S { int i; this(int i) { this.i = i; writeln(i, " is (about to be) alive!"); } ~this() { writeln(i, " is (already) dead."); } } void main() { foreach (i; 0 .. 3) { immutable s = S(i); } } The output: 0 is (about to be) alive! 0 is (already) dead. 1 is (about to be) alive! 1 is (already) dead. 2 is (about to be) alive! 2 is (already) dead. Module-level immutable and 'static const' would live much longer but they have a lifetime as well. However, today, the destructor cannot be executed on an immutable object, so I remove the qualifiers with cast(), which sohuld be undefined behavior (today?). immutable(S) moduleS; shared static this() { moduleS = S(42); } shared static ~this() { destroy(cast()moduleS); } void main() { } > Once instantiated > immutable data persists for the remainder of the program. That seems to be the misunderstanding. Again, I think module-level 'immutable' and 'static const' data fits that description. > You may not > have access if the variable goes out of scope, but if you do it will > always be there and always return the same value when you read from memory. That description fits D's GC-owned data (including immutables). The lifetime ends when there is no reference to it. Another example is immutable messages passed between threads with std.concurrency: That kind of data clearly originates at run time and the receiving end keeps the data alive as long as it needs. Ali
Re: Why do immutable variables need reference counting?
On Sunday, 10 April 2022 at 23:19:47 UTC, rikki cattermole wrote: immutable isn't tied to lifetime semantics. It only says that this memory will never be modified by anyone during its lifetime. This is clearly where I am misunderstanding. In my mind immutable data means the data will not change and neither will the result of reading that data, ever. I don't get how you can have thread safety guarantees based on immutable if reading that data in a thread suddenly becomes undefined behaviour and could return anything. That isn't immutable then. Once instantiated immutable data persists for the remainder of the program. You may not have access if the variable goes out of scope, but if you do it will always be there and always return the same value when you read from memory. Thanks for replying, I am not trying to be argumentative here, just stating what I thought it meant and why I am confused. I'll be doing some more reading of the D spec to better understand immutability. Cheers, Norm
Re: Why do immutable variables need reference counting?
immutable isn't tied to lifetime semantics. It only says that this memory will never be modified by anyone during its lifetime. Anyway, the real problem is with const. Both mutable and immutable become it automatically.
Why do immutable variables need reference counting?
Hi All, I am clearly misunderstanding something fundamental, and probably obvious :D Reading some of the discussions on __metadata I was wondering if someone could explain why a immutable reference counting type is needed. By definition a reference counter cannot be immutable, so what would be the use case that requires it? It cannot really be pure nor safe either because the ref goes out of scope and the allocation is freed. How is this immutable? Thanks, Norm
Re: How do you properly use immutable on class members?
On Tuesday, 29 March 2022 at 18:59:41 UTC, H. S. Teoh wrote: On Tue, Mar 29, 2022 at 05:58:11PM +, Fruitful Approach via Digitalmars-d-learn wrote: [...] 1) `immutable immutable(Prop)[]` is identical to `immutable(Prop[])`. Immutable is transitive. There is no need to spell it in such a verbose way. [...] Thank you thank you thank you! You've more than answered my concerns about using them. So you're saying I should make my ctor parameters either const or `in`. I will code some more and see if I can make this hurdle.
Re: How do you properly use immutable on class members?
On Tuesday, 29 March 2022 at 19:26:51 UTC, Ali Çehreli wrote: Better yet, and as I know you know :), and as it comes up occasionally but I usually forget in my own code; 'in' is much better than 'const' on function parameters because it has super powers when compiled with -preview=in: Hello to everyone. I think const is a redundant thing. Can you show me a direct side-effect that happens when we don't use const? SDB@79
Re: How do you properly use immutable on class members?
On 3/29/22 10:58, Fruitful Approach wrote: > so I end up undoing all the places I inserted it. That's what I do as well, which proves my 'const' object only hoped to be 'const'. I never say "I have to respect my decision from 10 minutes ago, so I must keep this object const." Nope! 'const' is replaced with 'auto' instantly. :) 'const' and 'immutable' on member variables are trouble because they make objects unassignable. (But you said 'class', so there is no such issue with them in D anyway.) But is that too bad? Is assignment overrated anyway? I don't know... :/ Ali
Re: How do you properly use immutable on class members?
On 3/29/22 11:59, H. S. Teoh wrote: > As a general principle, const should be used when you're on the > receiving end of data that should not be changed (e.g., function > parameters) Better yet, and as I know you know :), and as it comes up occasionally but I usually forget in my own code; 'in' is much better than 'const' on function parameters because it has super powers when compiled with -preview=in: https://dlang.org/spec/function.html#in-params Ali
Re: How do you properly use immutable on class members?
On Tue, Mar 29, 2022 at 05:58:11PM +, Fruitful Approach via Digitalmars-d-learn wrote: > I have immutable members: > > `immutable immutable(Prop)[] axioms` which I can't initialize with > Prop[], so I call this "immutable-poisoning" of the rest of my code. > Can you provide a 10 line example of how to best work with immutable > types, specifically immutable members, together with ctor, and > accessor methods? 1) `immutable immutable(Prop)[]` is identical to `immutable(Prop[])`. Immutable is transitive. There is no need to spell it in such a verbose way. 2) Immutable fields can be initialized only in the ctor. 3) You *can* initialize immutable data with mutable data if it's a unique reference. Usually you get a unique reference by returning it from a pure function. For example: Prop[] makeProps() pure { return [ Prop.init ]; } immutable(Prop[]) axioms = makeProps(); // OK So in your case, what you could do is to initialize your immutable field in your ctor, like this: class MyClass { immutable(Prop[]) axioms; private Prop[] makeAxioms() pure { ... } this() { axioms = makeAxioms(); } } [...] > Every time I use const or immutable in C++ or D I end up failing with > too many weird compilation errors, so I end up undoing all the places > I inserted it. Should I just _not use_ immutable or const except for > on `const` method decoration where applicable? D's const/immutable and C++'s const are very different beasts. D's const and immutable are transitive, meaning that if your outer reference is const, then everything it refers to will also be const (we call it "turtles all the way down"). Also, the way const interacts with mutable/immutable: const / \ (mutable) immutable Both mutable and immutable implicitly convert to const, but const does not implicitly convert to mutable/immutable (in general; there are exceptions like mutable -> immutable when it's a unique reference returned from a pure function, e.g. above). Basically, const means "I'm not allowed to change this, but somebody else might". Immutable means "I'm not allowed to change this, neither is anybody else." As a general principle, const should be used when you're on the receiving end of data that should not be changed (e.g., function parameters); immutable should be used when you're the owner of data that should not be change. T -- Holding a grudge is like drinking poison and hoping the other person dies. -- seen on the 'Net
How do you properly use immutable on class members?
I have immutable members: `immutable immutable(Prop)[] axioms` which I can't initialize with Prop[], so I call this "immutable-poisoning" of the rest of my code. Can you provide a 10 line example of how to best work with immutable types, specifically immutable members, together with ctor, and accessor methods? Also do I need to do `immutable immutable(Prop)[]` - that seems kind of wordy, especially when I have to duplicate that typing elsewhere. I could use an alias called `Axioms` I guess. Every time I use const or immutable in C++ or D I end up failing with too many weird compilation errors, so I end up undoing all the places I inserted it. Should I just _not use_ immutable or const except for on `const` method decoration where applicable? There's no good tutorial on a realistic immutable example AFAIK.
Re: This is bug or not? (immutable class containing struct with dtor)
On Saturday, 18 December 2021 at 12:50:17 UTC, Tejas wrote: As Ali said, this is an implementation issue. So I guess the answer to your question is that this is a bug. Please file a report at [issues.dlang.org](issues.dlang.org) Looks like this is same case: https://issues.dlang.org/show_bug.cgi?id=13628
Re: This is bug or not? (immutable class containing struct with dtor)
On Saturday, 18 December 2021 at 11:01:53 UTC, Denis Feklushkin wrote: On Friday, 17 December 2021 at 19:03:05 UTC, Tejas wrote: Well, I got completely mislead by my experiment ```d struct S { ~this() immutable {} } ``` Interesting what discussed behaviour isn't affects method what implements same functionality as dtor and called explictly at each appropriate place. So for dirty fix I just created ```d void __custom_dtor() const { ... } ``` And then called this __custom_dtor at each dtor what uses this struct. As Ali said, this is an implementation issue. So I guess the answer to your question is that this is a bug. Please file a report at [issues.dlang.org](issues.dlang.org)
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 19:03:05 UTC, Tejas wrote: Well, I got completely mislead by my experiment ```d struct S { ~this() immutable {} } ``` Interesting what discussed behaviour isn't affects method what implements same functionality as dtor and called explictly at each appropriate place. So for dirty fix I just created ```d void __custom_dtor() const { ... } ``` And then called this __custom_dtor at each dtor what uses this struct.
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 18:51:56 UTC, Ali Çehreli wrote: On 12/17/21 10:01 AM, Tejas wrote: > [...] Storage, There is no such requirement nor guarantee. [...] Well, I got completely mislead by my experiment ```d struct S { ~this() immutable {} } void main() { immutable S s = S(); } ``` This failed, so I just came up with reasons to justify this behaviour Thanks for correcting me
Re: This is bug or not? (immutable class containing struct with dtor)
On 12/17/21 10:01 AM, Tejas wrote: > I think since `immutable` objects are kept in Read Only Storage, There is no such requirement nor guarantee. > you > can't call destructors on them Destructor is nothing but a piece of code that is executed when an object's life ends. A destructor need not touch any member of the object: struct S { ~this() { import std.stdio; writeln("done"); } } void main() { immutable a = S(); auto b = immutable(S)(); } Both objects are immutable there yet their destructor is executed. > since the objects don't get erased when > `~this` is called, but rather they get assigned their `.init` value, That's true only for destroy(), which gets called only if the programmer asks for it. Otherwise, destroyed objects don't get assigned any special value. > which tells the GC that they can be collected. That's not true. The GC collects objects when there are no references to them. The values of the object's members have nothing to do with it. > `immutable class` has nothing to do with it, even the following fails to > compile: > ```d > > > > struct S > { > ~this() immutable {} That immutable qualifier means "this destructor is for immutable objects of this type." However, it seems impossible to define two destructors: ~this() { writeln(__FUNCTION__); } ~this() immutable { writeln(__FUNCTION__); } Error: destructor `deneme.S.~this` conflicts with destructor `deneme.S.~this` at deneme.d(79) I think this is an unexplored corner of the language. Part of the complication may be due to implementations by an earlier compiler contributor, who I heard was responsible for qualifiers on constructors. Note two different constructors here: import std.stdio; struct S { this(int) { writeln(__PRETTY_FUNCTION__); } this(int) immutable { writeln(__PRETTY_FUNCTION__); } } void main() { auto a = immutable(S)(0); auto b = S(1); } I bet the problem here is that the implementation in the compiler is half-baked on these qualifiers. Ali
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 18:32:43 UTC, Denis Feklushkin wrote: On Friday, 17 December 2021 at 18:02:52 UTC, Tejas wrote: I improved your sample: ```d immutable struct S { ~this() {} } immutable struct S2 { S sss; ~this() {} } void main() { S2 s = S2(); } ``` ``` Error: `immutable` method `serializer_bug.S.~this` is not callable using a mutable object Error: mutable method `serializer_bug.S2.~this` is not callable using a `immutable` object serializer_bug.d(17,5):Consider adding `const` or `inout` here ``` immutable dtor can't be called at all? Nope, seems to go against the very promise it's making Labelling `~this()` as const or immutable means it won't affect the state of the object, but it will, by it's very nature. That's why I said it's not too much of a stretch to imagine why they're disallowed entirely.
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 18:02:52 UTC, Tejas wrote: I improved your sample: ```d immutable struct S { ~this() {} } immutable struct S2 { S sss; ~this() {} } void main() { S2 s = S2(); } ``` ``` Error: `immutable` method `serializer_bug.S.~this` is not callable using a mutable object Error: mutable method `serializer_bug.S2.~this` is not callable using a `immutable` object serializer_bug.d(17,5):Consider adding `const` or `inout` here ``` immutable dtor can't be called at all?
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 18:19:34 UTC, Denis Feklushkin wrote: On Friday, 17 December 2021 at 18:01:03 UTC, Tejas wrote: I think since `immutable` objects are kept in Read Only Storage Some of them can be stored in ROM in some cases, but actually "immutable" keyword means "not mutable for whole its lifetime" Well, it would be really weird if destructors successfully executed for some class of `immutable` qualified objects but didn't for others. Not too much of a stretch to imagine that destruction for immutable objects was outright disallowed. Someone who can explain this behaviour more thoroughly would be much appreciated Maybe we should allow finalizers to mutate their instance?
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 18:01:03 UTC, Tejas wrote: I think since `immutable` objects are kept in Read Only Storage Some of them can be stored in ROM in some cases, but actually "immutable" keyword means "not mutable for whole its lifetime"
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 18:01:03 UTC, Tejas wrote: On Friday, 17 December 2021 at 17:34:05 UTC, Denis Feklushkin wrote: On Friday, 17 December 2021 at 17:27:53 UTC, Denis Feklushkin wrote: [...] ("serializer_bug" is just name of my local .d file) I think since `immutable` objects are kept in Read Only Storage, you can't call destructors on them since the objects don't get erased when `~this` is called, but rather they get assigned their `.init` value, which tells the GC that they can be collected. `immutable class` has nothing to do with it, even the following fails to compile: ```d struct S { ~this() immutable {} } void main() { S s = S(); } Error: `immutable` method `onlineapp.S.~this` is not callable using a mutable object ``` Correction: `immutable S s = S();` inside the `void main()`
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 17:34:05 UTC, Denis Feklushkin wrote: On Friday, 17 December 2021 at 17:27:53 UTC, Denis Feklushkin wrote: ~this() {} // Comment out this to fix this compilation error: // Error: `immutable` method `serializer_bug.Imm.~this` is ("serializer_bug" is just name of my local .d file) I think since `immutable` objects are kept in Read Only Storage, you can't call destructors on them since the objects don't get erased when `~this` is called, but rather they get assigned their `.init` value, which tells the GC that they can be collected. `immutable class` has nothing to do with it, even the following fails to compile: ```d struct S { ~this() immutable {} } void main() { S s = S(); } Error: `immutable` method `onlineapp.S.~this` is not callable using a mutable object ```
Re: This is bug or not? (immutable class containing struct with dtor)
On Friday, 17 December 2021 at 17:27:53 UTC, Denis Feklushkin wrote: ~this() {} // Comment out this to fix this compilation error: // Error: `immutable` method `serializer_bug.Imm.~this` is ("serializer_bug" is just name of my local .d file)
This is bug or not? (immutable class containing struct with dtor)
```d /+ dub.json: { "name": "test", "dependencies": { } } +/ struct S { ~this() {} } immutable class Imm { S s; // this is immutable value because whole class is immutable this() { s = S(); } ~this() {} // Comment out this to fix this compilation error: // Error: `immutable` method `serializer_bug.Imm.~this` is not callable using a mutable object // What mutable object is meant here? } void main() { auto ic = new immutable Imm(); } ``` Run: $ dub --single bug.d
Re: Efficient way to create/copy immutable struct instance with modified data
On Friday, 19 November 2021 at 17:38:53 UTC, Merlin Diavova wrote: I'm trying to figure out the most efficient way to create modified instances of immutable structs. This might not be the best way but it is a kinda cool trick anyway: structs have a `tupleof` property you can slice. So you can do things like: Node(old_node.tupleof[0 .. argIndex], replacement, old_node.tupleof[argIndex + 1 .. $]); Where argIndex can be like 0 to replace the first item in the struct, or 1 to replace the second. It is possible to look this up using reflection and kinda automate this if you wanted. ``` mixin template Replaceable() { // returns a replaced thing when you use it as `with_someField` typeof(this) opDispatch(string s, T)(T replacement) if(s.length > 5 && s[0 .. 5] == "with_") { static foreach(idx, member; typeof(this).tupleof) { static if(__traits(identifier, member) == s[5 .. $]) enum argIndex = idx; } // if the arg is not found you will get an ugly error message here about undefined // argIndex meh. return typeof(this)(this.tupleof[0 .. argIndex], replacement, this.tupleof[argIndex + 1 .. $]); } } immutable struct Node { string label; Node* parentNode; int port; mixin Replaceable; // add the ugly generic code from the top } void main() { Node i = Node("one", null, 4); Node i2 = i.with_port(6); // and now use it like this assert(i2.label == "one"); assert(i2.port == 6); } ``` I did `with_` instead of camelCase to avoid having to mess with case comparisons on the name.
Efficient way to create/copy immutable struct instance with modified data
Hi all, I'm trying to figure out the most efficient way to create modified instances of immutable structs. Currently, I'm doing the following: ```d immutable struct Node { string label; Node parentNode; NetworkPort port; auto withLabel(string newLabel) { return Node(newLabel, this.parentNode, this.port); } auto withParentNode(Node newParentNode) { return Node(this.label, newParentNode, this.port); } auto withNetworkPort(NetworkPort newPort) { return Node(this.label, this.parentNode, newPort); } } ``` Coming from a scripting language the above makes the most sense. In D, this an efficient way to do it? Are there any best practices around this? Thanks in advance Merlin
Re: Run-time setting of immutable variable?
On Thursday, 2 September 2021 at 23:12:28 UTC, Steven Schveighoffer wrote: [...] immutable means "I can never change and *everything I point at* can never change". [...] If that is how the language defines the keyword 'immutable' when used in the definition of a pointer variable, then so be it. I would, however, suggest that the additional 'action-at-a-distance' implication (freezing not just the variable itself, but also it's target) is inconsistent with the definition of 'immutable' with other variable types. Surely it would be better to reserve 'immutable' on a pointer to mean simply set-once on the pointer itself (with no implications for whatever the pointer is pointing to), and another keyword ('blocked'?) for the current definition?
Re: Run-time setting of immutable variable?
On 9/2/21 9:01 AM, DLearner wrote: Suppose there is a variable that is set once per run, and is (supposed) never to be altered again. An accessor function can be a solution, which supports your other comment about data potentially mutating by other means: // Assume these are in a module // // This is private; you can even name it arr_. private ubyte[10] arr; // This is the accessor: public const(ubyte)* arrPtr() { return arr.ptr; } // void main() { *arrPtr = 42; // Compilation ERROR; good. } Ali
Re: Run-time setting of immutable variable?
On 9/2/21 1:17 PM, DLearner wrote: I am looking for a mutable Arr but would like an immutable ArrPtr. Then you want const not immutable. Here is the reason: ```d void main() { int x = 5; immutable int *ptr = cast(immutable int *) assert(*ptr == 5); // ok x = 6; assert(*ptr == 5); // what happens here? } ``` Depending on optimizations and compiler constant folding, that second assert may pass. immutable means "I can never change and *everything I point at* can never change". The compiler is free to use this knowledge to avoid redoing calculations it has already done. I tknows that `*ptr == 5` already, and that can never change, so it just doesn't even bother with the second assert. However, make ptr *const*, and now not only do you not need the cast, but the second assert will fail as expected. ``` `Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's immutable. ``` Surely there is no inconsistency - at run time the array is in a fixed place, so ArrPtr is (or at least should be) a constant, but the contents of the array can vary as the program runs. In D, const and immutable are transitive, which means that you can't have a pointer that is const, but allows changing what it points at. So while a const pointer *may* work for your purposes, you may want a specialized type, or a property function. It depends on how you intend to use `ArrPtr`. Others have given good answers to this problem. -Steve
Re: Run-time setting of immutable variable?
On Thu, Sep 02, 2021 at 05:17:15PM +, DLearner via Digitalmars-d-learn wrote: [...] > The following clean-compiled and produced the expected result: > ``` > ubyte[10] Arr; > > immutable void* ArrPtr; > shared static this() { > ArrPtr = cast(immutable void*)([0]); > } Casting this to immutable is unsafe, because Arr is actually mutable. In D, const and immutable are transitive, so when you cast a pointer into immutable, you're basically promising that *both* the pointer *and* the data pointed to will never change. The compiler is free to assume that the data pointed to will never change, which may cause consistency problems if the data *does* change later. In cases like this, const is a better choice: const ensures that the pointer will not mutate and the pointed-to data cannot be mutated through this pointer, but the data *may* be mutated through another reference (e.g., by modifying Arr directly). [...] > I am looking for a mutable Arr but would like an immutable ArrPtr. In D, const and immutable are transitive, so you cannot do this (but see below). > ``` > `Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's > immutable. > ``` > Surely there is no inconsistency - at run time the array is in a fixed > place, so ArrPtr is (or at least should be) a constant, but the > contents of the array can vary as the program runs. In this case, what you want is const, not immutable. Const means you cannot modify the pointer, and you cannot mutate the data through this pointer, but you *can* modify the array from a mutable reference. T -- You are only young once, but you can stay immature indefinitely. -- azephrahel
Re: Run-time setting of immutable variable?
On Thursday, 2 September 2021 at 17:17:15 UTC, DLearner wrote: Surely there is no inconsistency - at run time the array is in a fixed place, so ArrPtr is (or at least should be) a constant, but the contents of the array can vary as the program runs. In the case of `immutable(T)* ArrPtr`, the contents of the pointed-to array cannot vary as the program runs. If it's immutable, nothing in the program ever mutates it. In the case of `const(T)* ArrPtr`, the contents of the pointed-to array can vary as the program runs, and the only restriction is that the array can't be mutated through ArrPtr. If what you mean is that you want the *pointer* to never change, `T * const ArrPtr` in C syntax, but that you don't care if the pointed-to array changes via ArrPtr or any other reference, then I don't think D can express this. You could make ArrPtr a function: ```d void main() { int[] xs = [1, 2, 3, 4]; int* p() { return [2]; } p[0] = 0; assert(xs == [1, 2, 0, 4]); p++; // Error: ... not an lvalue and cannot be modified } ```
Re: Run-time setting of immutable variable?
If you want only address, you can keep it as size_t: ubyte[10] Arr; immutable size_t Address; static this() { Address = cast(size_t)([0]); }
Re: Run-time setting of immutable variable?
On Thursday, 2 September 2021 at 16:46:46 UTC, Steven Schveighoffer wrote: On 9/2/21 12:01 PM, DLearner wrote: Suppose there is a variable that is set once per run, and is (supposed) never to be altered again. However, the value to which it is set is not known at compile time. Example below, variable is 'ArrPtr'; ``` ubyte[10] Arr; // immutable void* ArrPtr; void* ArrPtr; void main() { ArrPtr = cast(void*)Arr[0]; // modify ArrPtr> } ``` Is there a way of getting D to guarantee that ArrPtr is never modified after ``` ArrPtr = cast(void*)Arr[0]; ```] You shouldn't be doing this. `Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's immutable. If you want to guarantee that `ArrPtr` never changes once set, yet still want it to point at mutable data, you need to use a type that does that (like a head mutable or "write once" type), which I believe doesn't exist in phobos. If you don't actually need to mutate the data via `ArrPtr`, you can make it const, and use H.S. Teoh's solution. But you should not use immutable, as the compiler implies that data pointed at is also immutable (and of course, you won't need casting). Make sure you use regular `static this`, not `shared static this`, as your fields are thread-local, not shared. -Steve The following clean-compiled and produced the expected result: ``` ubyte[10] Arr; immutable void* ArrPtr; shared static this() { ArrPtr = cast(immutable void*)([0]); } void main() { import std.stdio; void* ArrPtr2; ArrPtr2 = cast(void*)([0]); writeln("ArrPtr = ", ArrPtr); writeln("ArrPtr2 = ", ArrPtr2); // consistency test // ArrPtr = ArrPtr + 1; // mutability test - compile (correctly) failed when uncommented. ArrPtr2 = ArrPtr2 + 1; Arr[1] = 4; Arr[1] = 7; // mutability test } ``` The following produced deprecated warnings, but still seemed to work: ``` ubyte[10] Arr; immutable void* ArrPtr; static this() { ArrPtr = cast(immutable void*)([0]); } void main() { import std.stdio; void* ArrPtr2; ArrPtr2 = cast(void*)([0]); writeln("ArrPtr = ", ArrPtr); writeln("ArrPtr2 = ", ArrPtr2); // consistency test // ArrPtr = ArrPtr + 1; // mutability test on ArrPtr - compile (correctly) failed when uncommented. ArrPtr2 = ArrPtr2 + 1; Arr[1] = 4; Arr[1] = 7; // mutability test on Arr } ``` I am looking for a mutable Arr but would like an immutable ArrPtr. ``` `Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's immutable. ``` Surely there is no inconsistency - at run time the array is in a fixed place, so ArrPtr is (or at least should be) a constant, but the contents of the array can vary as the program runs.
Re: Run-time setting of immutable variable?
On 9/2/21 12:01 PM, DLearner wrote: Suppose there is a variable that is set once per run, and is (supposed) never to be altered again. However, the value to which it is set is not known at compile time. Example below, variable is 'ArrPtr'; ``` ubyte[10] Arr; // immutable void* ArrPtr; void* ArrPtr; void main() { ArrPtr = cast(void*)Arr[0]; // } ``` Is there a way of getting D to guarantee that ArrPtr is never modified after ``` ArrPtr = cast(void*)Arr[0]; ```] You shouldn't be doing this. `Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's immutable. If you want to guarantee that `ArrPtr` never changes once set, yet still want it to point at mutable data, you need to use a type that does that (like a head mutable or "write once" type), which I believe doesn't exist in phobos. If you don't actually need to mutate the data via `ArrPtr`, you can make it const, and use H.S. Teoh's solution. But you should not use immutable, as the compiler implies that data pointed at is also immutable (and of course, you won't need casting). Make sure you use regular `static this`, not `shared static this`, as your fields are thread-local, not shared. -Steve
Re: Run-time setting of immutable variable?
On Thu, Sep 02, 2021 at 04:01:19PM +, DLearner via Digitalmars-d-learn wrote: > Suppose there is a variable that is set once per run, and is > (supposed) never to be altered again. However, the value to which it > is set is not known at compile time. This is the classic use case of `immutable`. Using the example you gave, you'd move the initialization of ArrPtr into a static module constructor: immutable void* ArrPtr; shared static this() { ArrPtr = ...; // initialize it here } void main() { ... // ArrPtr is immutable from here on. } T -- Дерево держится корнями, а человек - друзьями.
Run-time setting of immutable variable?
Suppose there is a variable that is set once per run, and is (supposed) never to be altered again. However, the value to which it is set is not known at compile time. Example below, variable is 'ArrPtr'; ``` ubyte[10] Arr; // immutable void* ArrPtr; void* ArrPtr; void main() { ArrPtr = cast(void*)Arr[0]; // ArrPtr> } ``` Is there a way of getting D to guarantee that ArrPtr is never modified after ``` ArrPtr = cast(void*)Arr[0]; ``` Best regards
Re: Creating immutable arrays in @safe code
On Sunday, 18 July 2021 at 10:02:07 UTC, ag0aep6g wrote: On 17.07.21 15:56, ag0aep6g wrote: +1. IMO,`strong pure` is `the outside world` has no impact on me. `Funcs` don't have any `indirect`.
Re: Creating immutable arrays in @safe code
On 17.07.21 15:56, ag0aep6g wrote: At a glance, the only meaningful use of `PURE.strong` seems to be in dcast.d, introduced by the PR you linked. Changing that to `PURE.const_` doesn't break any tests for me. So I'm inclined to believe that `PURE.strong` is nonsense, and that `PURE.const_` already means "strongly pure". However, changing that instance doesn't fix the issue. Apparently, DMD doesn't even recognize int[] array(const int[] input) pure { ... } as `PURE.const_`. I've dug a bit deeper, and apparently I'm to blame for confusing things. In issue 15862 [1], I stated that functions with mutable indirections in the return type cannot be strongly pure. That's wrong, but it seems to have found its way into DMD. The core of issue 15862 is true: Two calls to `array` cannot be merged into one. But that doesn't make it weakly pure. Mutability in the return type is distinct from weak/strong purity. These are all true: * `array` is "strongly pure". * `array` is a "pure factory function". * The result of one call to `array` cannot be reused for another, identical call. [1] https://issues.dlang.org/show_bug.cgi?id=15862.
Re: Creating immutable arrays in @safe code
On 17.07.21 14:56, Dennis wrote: On Saturday, 17 July 2021 at 12:05:44 UTC, ag0aep6g wrote: Hm, as far as I understand, "strongly pure" doesn't require `immutable` parameters. `const` should be enough. The spec says: "A strongly pure function has no parameters with mutable indirections" [1]. I just took the description from the source code: ```D enum PURE : ubyte { impure = 0, // not pure at all fwdref = 1, // it's pure, but not known which level yet weak = 2, // no mutable globals are read or written const_ = 3, // parameters are values or const strong = 4, // parameters are values or immutable } ``` That looks off to me. Unless DMD has some secret knowledge about a shortcoming in the established definition of "strongly pure", I think those enum values are badly named. At a glance, the only meaningful use of `PURE.strong` seems to be in dcast.d, introduced by the PR you linked. Changing that to `PURE.const_` doesn't break any tests for me. So I'm inclined to believe that `PURE.strong` is nonsense, and that `PURE.const_` already means "strongly pure". However, changing that instance doesn't fix the issue. Apparently, DMD doesn't even recognize int[] array(const int[] input) pure { ... } as `PURE.const_`. I don't know whether the spec or code is correct. When it comes to purity, another piece in the puzzle is David Nadlinger's article: https://klickverbot.at/blog/2012/05/purity-in-d/ There, a function with a `const int[]` parameter is described as "strongly pure". As far as I can remember, when DMD and that article disagreed in the past, DMD was wrong. [...] Yup, [remember this](https://github.com/dlang/dmd/pull/8035#discussion_r174771516)? Hehe, I knew I had complained about it before. It's doubly bad: (1) We're missing out on bug fixes, because they're hidden behind `-preview=dip1000` for no reason. (2) When `-preview=dip1000` ever becomes the default, code will break that doesn't even use the features of DIP 1000.
Re: Creating immutable arrays in @safe code
On 18/07/2021 12:56 AM, Dennis wrote: I don't know whether the spec or code is correct. Unless otherwise specified, the code is authoritative.
Re: Creating immutable arrays in @safe code
On Saturday, 17 July 2021 at 12:05:44 UTC, ag0aep6g wrote: Hm, as far as I understand, "strongly pure" doesn't require `immutable` parameters. `const` should be enough. The spec says: "A strongly pure function has no parameters with mutable indirections" [1]. I just took the description from the source code: ```D enum PURE : ubyte { impure = 0,// not pure at all fwdref = 1,// it's pure, but not known which level yet weak= 2,// no mutable globals are read or written const_ = 3,// parameters are values or const strong = 4,// parameters are values or immutable } ``` I don't know whether the spec or code is correct. Also, conflating other issues with DIP1000 is such an obviously terrible idea. Yup, [remember this](https://github.com/dlang/dmd/pull/8035#discussion_r174771516)?
Re: Creating immutable arrays in @safe code
On 17.07.21 13:05, Dennis wrote: There used to be a complex `isReturnIsolated` check, but the [fix for issue 15660](https://github.com/dlang/dmd/pull/8048) reduced it to a check 'is the function strongly `pure`' which means 'parameters are values or immutable'. To reduce code breakage, the 'strong pure' requirement is only needed with -dip1000, which is why your example doesn't work with it. Hm, as far as I understand, "strongly pure" doesn't require `immutable` parameters. `const` should be enough. The spec says: "A strongly pure function has no parameters with mutable indirections" [1]. Seems to me that the fix is buggy. Also, conflating other issues with DIP1000 is such an obviously terrible idea. [1] https://dlang.org/spec/function.html#pure-functions
Re: Creating immutable arrays in @safe code
On Saturday, 17 July 2021 at 05:44:24 UTC, ag0aep6g wrote: I tried doing that, but `-preview=dip1000` causes trouble. This fails: (...) I'm not sure what's going on. I'm not completely caught up, but from what I see, pure and immutable have a history of issues: [Issue 11503 - Type system breaking caused by implicit conversion for the value returned from pure function](https://issues.dlang.org/show_bug.cgi?id=11503) [Issue 11909 - Struct members and static arrays break pure function escape analysis (immutability violation)](https://issues.dlang.org/show_bug.cgi?id=11909) [Issue 15660 - break immutable with pure function and mutable reference params](https://issues.dlang.org/show_bug.cgi?id=15660) There used to be a complex `isReturnIsolated` check, but the [fix for issue 15660](https://github.com/dlang/dmd/pull/8048) reduced it to a check 'is the function strongly `pure`' which means 'parameters are values or immutable'. To reduce code breakage, the 'strong pure' requirement is only needed with -dip1000, which is why your example doesn't work with it.
Re: Creating immutable arrays in @safe code
On 17.07.21 00:27, H. S. Teoh wrote: Hmm, OK. Not sure why .array isn't being inferred as unique... but yeah, you probably have to resort to using @trusted with .assumeUnique. In addition to `pure`, you also need a const/immutable input and a mutable output, so that the output cannot be a slice of the input. For std.array.array it might be possible to carefully apply `Unqual` to the element type. I tried doing that, but `-preview=dip1000` causes trouble. This fails: int[] array(const int[] input) pure nothrow @safe { int[] output; foreach (element; input) output ~= element; return output; } void main() pure nothrow @safe { const int[] c = [1, 2, 3]; immutable int[] i = array(c); /* Without `-preview=dip1000`: works, because the result is unique. With `-preview=dip1000`: "Error: cannot implicitly convert". */ } I'm not sure what's going on. `pure` being involved makes me think of issue 20150. But it also fails with my fix for that issue. So maybe it's another bug.