www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - ldc executable crashes with this code

reply forkit <forkit gmail.com> writes:
Any reason why compiling this with ldc would cause the exe to 
crash?

Compiling with DMD (using either declaration of palindrome works 
just fine though)


// ----

module test;

import std;

void main()
{
     char[] palindrome = cast(char[])"able was I ere I saw elba";

    //char[] palindrome = 
['a','b','l','e','w','a','s','I','e','r','e','I','s','a','w','e','l','b','a'];

     writeln(palindrome);

     // note: The line below causes the exe to crash when compiled 
with ldc
     // but only if using the first version of palindrome.

     writeln(palindrome.reverse);
}

// ---
Feb 02 2022
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Feb 02, 2022 at 11:21:52PM +0000, forkit via Digitalmars-d-learn wrote:
[...]
     char[] palindrome = cast(char[])"able was I ere I saw elba";
String literals are immutable by default. Casting immutable to mutable is UB (Undefined Behaviour). [...]
     writeln(palindrome.reverse);
Especially because .reverse mutates its argument. So you're attempting to overwrite immutable data here. That's probably what caused the crash: the literal is put in the read-only segment and the OS killed the program when it tried to write to data in that read-only segment. T -- People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird. -- D. Knuth
Feb 02 2022
parent reply forkit <forkit gmail.com> writes:
On Wednesday, 2 February 2022 at 23:30:50 UTC, H. S. Teoh wrote:
 On Wed, Feb 02, 2022 at 11:21:52PM +0000, forkit via 
 Digitalmars-d-learn wrote: [...]
     char[] palindrome = cast(char[])"able was I ere I saw 
 elba";
String literals are immutable by default. Casting immutable to mutable is UB (Undefined Behaviour). [...]
     writeln(palindrome.reverse);
Especially because .reverse mutates its argument. So you're attempting to overwrite immutable data here. That's probably what caused the crash: the literal is put in the read-only segment and the OS killed the program when it tried to write to data in that read-only segment. T
that explains ldc perhaps (although i don't really get it. It's cast to mutable and being assigned to mutable. in any case... ldc doesn't like it, but dmd is fine with this ??
Feb 02 2022
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Thursday, 3 February 2022 at 01:39:33 UTC, forkit wrote:
 On Wednesday, 2 February 2022 at 23:30:50 UTC, H. S. Teoh wrote:
 [...]
that explains ldc perhaps (although i don't really get it. It's cast to mutable and being assigned to mutable. in any case... ldc doesn't like it, but dmd is fine with this ??
your cast from immutable to mutable is an undefined behavior, this may work or not.
 Note that casting away a const qualifier and then mutating is 
 undefined behavior, too, even when the referenced data is 
 mutable. This is so that compilers and programmers can make 
 assumptions based on const alone. For example, here it may be 
 assumed that f does not alter x:
(from https://dlang.org/spec/const3.html#removing_with_cast)
Feb 02 2022
parent Basile B. <b2.temp gmx.com> writes:
On Thursday, 3 February 2022 at 01:51:34 UTC, Basile B. wrote:
 On Thursday, 3 February 2022 at 01:39:33 UTC, forkit wrote:
 On Wednesday, 2 February 2022 at 23:30:50 UTC, H. S. Teoh 
 wrote:
 [...]
that explains ldc perhaps (although i don't really get it. It's cast to mutable and being assigned to mutable. in any case... ldc doesn't like it, but dmd is fine with this ??
your cast from immutable to mutable is an undefined behavior, this may work or not.
 Note that casting away a const qualifier and then mutating is 
 undefined behavior, too, even when the referenced data is 
 mutable. This is so that compilers and programmers can make 
 assumptions based on const alone. For example, here it may be 
 assumed that f does not alter x:
(from https://dlang.org/spec/const3.html#removing_with_cast)
the D safe way : ``` void main() safe { char[] palindrome = "able was I ere I saw elba".dup; writeln(palindrome); writeln(palindrome.reverse); } ```
Feb 02 2022
prev sibling next sibling parent forkit <forkit gmail.com> writes:
On Thursday, 3 February 2022 at 01:39:33 UTC, forkit wrote:

oops!  forgot the .dup

char[] palindrome = cast(char[])"able was I ere I saw elba".dup;

;-)
Feb 02 2022
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Feb 03, 2022 at 01:39:33AM +0000, forkit via Digitalmars-d-learn wrote:
 On Wednesday, 2 February 2022 at 23:30:50 UTC, H. S. Teoh wrote:
 On Wed, Feb 02, 2022 at 11:21:52PM +0000, forkit via Digitalmars-d-learn
 wrote: [...]
     char[] palindrome = cast(char[])"able was I ere I saw elba";
String literals are immutable by default. Casting immutable to mutable is UB (Undefined Behaviour). [...]
     writeln(palindrome.reverse);
Especially because .reverse mutates its argument. So you're attempting to overwrite immutable data here. That's probably what caused the crash: the literal is put in the read-only segment and the OS killed the program when it tried to write to data in that read-only segment.
[...]
 that explains ldc perhaps (although i don't really get it. It's cast
 to mutable and being assigned to mutable.
Assigning the literal to char[] simply creates a mutable slice of the immutable data. That's technically UB. Then .reverse modifies the array in-place, which means you violated immutable.
 in any case... ldc doesn't like it, but dmd is fine with this ??
UB doesn't mean a guaranteed crash, it means the result will be implementation-dependent (and likely not what was intended). It's possible that dmd didn't put the literal in a read-only segment, or there may be some other reason. In any case, just because it worked by chance does not mean it's OK to simply cast immutable to mutable and then proceed to mutate it. T -- Let X be the set not defined by this sentence...
Feb 02 2022
parent reply forkit <forkit gmail.com> writes:
On Thursday, 3 February 2022 at 01:57:12 UTC, H. S. Teoh wrote:

would be nice if the compiler told me something though :-(

i.e. "hey, dude, you really wanna to that?"
Feb 02 2022
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Feb 03, 2022 at 02:01:34AM +0000, forkit via Digitalmars-d-learn wrote:
[...]
 would be nice if the compiler told me something though :-(
 
 i.e. "hey, dude, you really wanna to that?"
Mark your function safe, and the compiler will stop you from unsafe casts of this nature. That's part of the reason we have safe. ;-) T -- Everybody talks about it, but nobody does anything about it! -- Mark Twain
Feb 02 2022
parent reply forkit <forkit gmail.com> writes:
On Thursday, 3 February 2022 at 03:25:39 UTC, H. S. Teoh wrote:
 On Thu, Feb 03, 2022 at 02:01:34AM +0000, forkit via 
 Digitalmars-d-learn wrote: [...]
 would be nice if the compiler told me something though :-(
 
 i.e. "hey, dude, you really wanna to that?"
Mark your function safe, and the compiler will stop you from unsafe casts of this nature. That's part of the reason we have safe. ;-) T
so i mark all my modules as safe, by default. I commented it out though, so I could do the cast. Then realised I didn't need the cast at all, just the .dup now it's safe again. But safe or not, nothing good can come from casting an immutable string to a mutable string, and the compiler really should know that ;-)
Feb 02 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/3/22 12:53 AM, forkit wrote:

 But  safe or not, nothing good can come from casting an immutable string 
 to a mutable string, and the compiler really should know that ;-)
 
If you have a function that accepts mutable data, but you know for that call it won't mutate the data (or maybe it's a C function that never mutates the data, but isn't attributed with const/immutable), then it is not UB. It's only UB to cast away immutable/const *and* mutate it. Reference: https://dlang.org/spec/const3.html#removing_with_cast -Steve
Feb 03 2022
prev sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 3 February 2022 at 02:01:34 UTC, forkit wrote:
 On Thursday, 3 February 2022 at 01:57:12 UTC, H. S. Teoh wrote:

 would be nice if the compiler told me something though :-(

 i.e. "hey, dude, you really wanna to that?"
would be nice if programmers (C or D) learnt that a typecast means "shut up compiler I know what I do". You explicitly instructed the compiler to not complain. Remove the typecast and the compiler will bring an error. That's the reason why typecasts are to be avoided as much as possible.It is often a code smell.
Feb 04 2022
next sibling parent forkit <forkit gmail.com> writes:
On Friday, 4 February 2022 at 10:09:22 UTC, Patrick Schluter 
wrote:
 On Thursday, 3 February 2022 at 02:01:34 UTC, forkit wrote:
 On Thursday, 3 February 2022 at 01:57:12 UTC, H. S. Teoh wrote:

 would be nice if the compiler told me something though :-(

 i.e. "hey, dude, you really wanna to that?"
would be nice if programmers (C or D) learnt that a typecast means "shut up compiler I know what I do". You explicitly instructed the compiler to not complain. Remove the typecast and the compiler will bring an error. That's the reason why typecasts are to be avoided as much as possible.It is often a code smell.
In C, I would have no such expectation. The cast will occur whether I typed it in or not. My problem was, that I forgot the .dup For the compiler to allow me to cast from immutable to mutable (without the .dup), makes about as much sense as the compiler allowing this: int i; i = "Hello";
Feb 04 2022
prev sibling parent reply forkit <forkit gmail.com> writes:
On Friday, 4 February 2022 at 10:09:22 UTC, Patrick Schluter 
wrote:
 On Thursday, 3 February 2022 at 02:01:34 UTC, forkit wrote:
 On Thursday, 3 February 2022 at 01:57:12 UTC, H. S. Teoh wrote:

 would be nice if the compiler told me something though :-(

 i.e. "hey, dude, you really wanna to that?"
would be nice if programmers (C or D) learnt that a typecast means "shut up compiler I know what I do". You explicitly instructed the compiler to not complain. Remove the typecast and the compiler will bring an error. That's the reason why typecasts are to be avoided as much as possible.It is often a code smell.
If I had wrote the code below, then I should not expect anything, whatsoever, from the compiler. () trustMe_I_am_a_complete_idiot { char[] palindrome = cast(char[])"able was I ere I saw elba"; } ();
Feb 04 2022
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Friday, 4 February 2022 at 11:26:42 UTC, forkit wrote:

 If I had wrote the code below, then I should not expect 
 anything, whatsoever, from the compiler.

 ()  trustMe_I_am_a_complete_idiot { char[] palindrome = 
 cast(char[])"able was I ere I saw elba";  } ();
This is almost exactly what you have to do today, assuming you're using safe. You'll **have** to write this then: `() trusted { auto palindrome = cast(char[])"able was I ere I saw elba"; } ();` Instead, you opted to comment out ` safe`. Unfortunately, " safe by default" idea was brutally eviscerated. As others have already stated, casting immutability away is something that has to be supported, e.g. to interface with const-agnostic APIs. ` safe` requires such casts to be more verbose, with good reason.
Feb 04 2022
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Feb 04, 2022 at 03:58:19PM +0000, Stanislav Blinov via
Digitalmars-d-learn wrote:
[...]
 As others have already stated, casting immutability away is something
 that has to be supported, e.g. to interface with const-agnostic APIs.
 ` safe` requires such casts to be more verbose, with good reason.
Not to mention, casting to/from immutable is necessary in order to be able to implement D's GC in D. Basically, a cast is equivalent to telling the compiler "I know what I'm doing, don't complain". Generally casts should be avoided except under special circumstances. T -- Fact is stranger than fiction.
Feb 04 2022
prev sibling parent reply forkit <forkit gmail.com> writes:
On Friday, 4 February 2022 at 15:58:19 UTC, Stanislav Blinov 
wrote:
 ..
 ...
 As others have already stated, casting immutability away is 
 something that has to be supported, e.g. to interface with 
 const-agnostic APIs. ` safe` requires such casts to be more 
 verbose, with good reason.
I concede ;-) That the compiler knows this is safe: cast(char[])iStr.dup; and this is not safe: cast(char[])iStr; is sufficent.
Feb 04 2022
parent bauss <jj_1337 live.dk> writes:
On Saturday, 5 February 2022 at 03:02:37 UTC, forkit wrote:
 On Friday, 4 February 2022 at 15:58:19 UTC, Stanislav Blinov 
 wrote:
 ..
 ...
 As others have already stated, casting immutability away is 
 something that has to be supported, e.g. to interface with 
 const-agnostic APIs. ` safe` requires such casts to be more 
 verbose, with good reason.
I concede ;-) That the compiler knows this is safe: cast(char[])iStr.dup; and this is not safe: cast(char[])iStr; is sufficent.
The compiler doesn't know what you think it knows in this scenario. It doesn't know what function you intended to use originally, in this case .dup. So what do you expect the compiler to tell you when you do the following? ```d cast(char[])iStr ``` It's not actually invalid code and only invalid under safe IFF the result is mutated. To the compiler everything is actually fine in that scenario. The compiler also doesn't know that when you cast to char[] that you want to get a mutable duplicate of the string, because it doesn't have such a concept. It doesn't know .dup is actually the function you want to call, because there could be an ocean of different functions that you want to call that returns a mutable reference of the type. In fact by giving it a cast you tell the compiler "I know what type this really is" and thus the compiler just says "okay". It won't complain when you tell it you know what you're doing, similarly to calling a trusted function under safe, you tell the compiler "I know this function is safe, even though it's not marked as safe" and thus the compiler will not actually complain if the function wasn't safe.
Feb 07 2022
prev sibling next sibling parent reply Tejas <notrealemail gmail.com> writes:
On Wednesday, 2 February 2022 at 23:21:52 UTC, forkit wrote:
 Any reason why compiling this with ldc would cause the exe to 
 crash?

 Compiling with DMD (using either declaration of palindrome 
 works just fine though)


 // ----

 module test;

 import std;

 void main()
 {
     char[] palindrome = cast(char[])"able was I ere I saw elba";

    //char[] palindrome = 
 ['a','b','l','e','w','a','s','I','e','r','e','I','s','a','w','e','l','b','a'];

     writeln(palindrome);

     // note: The line below causes the exe to crash when 
 compiled with ldc
     // but only if using the first version of palindrome.

     writeln(palindrome.reverse);
 }

 // ---
This segfaults even on `dmd 2.098.0` for me. Clearly implementation defined behavior.
Feb 02 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/2/22 10:07 PM, Tejas wrote:
 On Wednesday, 2 February 2022 at 23:21:52 UTC, forkit wrote:
 Any reason why compiling this with ldc would cause the exe to crash?

 Compiling with DMD (using either declaration of palindrome works just 
 fine though)


 // ----

 module test;

 import std;

 void main()
 {
     char[] palindrome = cast(char[])"able was I ere I saw elba";

    //char[] palindrome = 
 ['a','b','l','e','w','a','s','I','e','r','e','I','s','a','w','e','l','b','a']; 


     writeln(palindrome);

     // note: The line below causes the exe to crash when compiled with 
 ldc
     // but only if using the first version of palindrome.

     writeln(palindrome.reverse);
 }

 // ---
This segfaults even on `dmd 2.098.0` for me. Clearly implementation defined behavior.
Note that on Windows DMD, string literals are not marked as Read only. This is legacy from the D1 days when strings were actually typed as `char[]`. In fact, if you mutate the data, then it mutated the literal, so if you used the literal later, then you got the mutated version! e.g.: ```d auto str = "foo"; str[0] = 'b'; writeln("foo"); // outputs "boo" ``` On Linux, the literals were marked as ROM, and so you get a runtime fault. If you search back to the D1 days you can find posts about this difference between Windows and Linux. I'm assuming LDC and GDC follow the same practice as Linux DMD. -Steve
Feb 03 2022
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
I know we are talking about something else but just to make sure, there 
is no need for the cast, copy, or .reverse:

      const palindrome = "able was I ere I saw elba";
      writeln(palindrome);
      writeln(palindrome.retro);

Ali
Feb 03 2022