www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - String copying fails when output from assert

reply David Zhang <straivers98 gmail.com> writes:
Hi,

I've been trying to copy a string, then output it from an 
`assert(false)` statement, but the copy seems to be corrupted 
somehow.

void main()
{
	string str = "some string";

         //initializing `chars` with any value doesn't do anything
	char[64] chars;
	//char[64] chars = '!';

	//memcpy doesn't work either, though the output's different
	//import core.stdc.string;
	//memcpy(&chars[0], &str[0], str.length);

	chars[0..str.length] = str;
	assert(false, chars[0..str.length]);
}

What am I missing here?
Nov 21
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 21 November 2017 at 18:49:40 UTC, David  Zhang wrote:
 	assert(false, chars[0..str.length]);
 }

 What am I missing here?
You're escaping a reference to a local variable there. chars[] is a pointer to the stack, which is promptly smashed when the assert error starts working up the call chain, so by the time it actually prints, you have undefined behavior.
Nov 21
parent reply David Zhang <straivers98 gmail.com> writes:
On Tuesday, 21 November 2017 at 18:56:03 UTC, Adam D. Ruppe wrote:
 On Tuesday, 21 November 2017 at 18:49:40 UTC, David  Zhang 
 wrote:
 	assert(false, chars[0..str.length]);
 }

 What am I missing here?
You're escaping a reference to a local variable there. chars[] is a pointer to the stack, which is promptly smashed when the assert error starts working up the call chain, so by the time it actually prints, you have undefined behavior.
So I'd have to allocate the buffer on the heap then... Is there any way to do this without allocating to the stack?
Nov 21
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, November 21, 2017 18:59:14 David Zhang via Digitalmars-d-learn 
wrote:
 On Tuesday, 21 November 2017 at 18:56:03 UTC, Adam D. Ruppe wrote:
 On Tuesday, 21 November 2017 at 18:49:40 UTC, David  Zhang

 wrote:
    assert(false, chars[0..str.length]);

 }

 What am I missing here?
You're escaping a reference to a local variable there. chars[] is a pointer to the stack, which is promptly smashed when the assert error starts working up the call chain, so by the time it actually prints, you have undefined behavior.
So I'd have to allocate the buffer on the heap then... Is there any way to do this without allocating to the stack?
Why would you need to allocate on the stack to allocate on the heap? If you have a string to pass assert for its message, then just pass it the string. If you have a char[], then idup it. If you have a static array on the stack, then idup it. And if you're iduping in the assert statement, then it should only idup when the assertion fails. - Jonathan M Davis
Nov 21
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, November 21, 2017 18:49:40 David Zhang via Digitalmars-d-learn 
wrote:
 Hi,

 I've been trying to copy a string, then output it from an
 `assert(false)` statement, but the copy seems to be corrupted
 somehow.

 void main()
 {
   string str = "some string";

          //initializing `chars` with any value doesn't do anything
   char[64] chars;
   //char[64] chars = '!';

   //memcpy doesn't work either, though the output's different
   //import core.stdc.string;
   //memcpy(&chars[0], &str[0], str.length);

   chars[0..str.length] = str;
   assert(false, chars[0..str.length]);
 }

 What am I missing here?
Well, the assertion is going to throw an AssertError, which takes a string for its message. It doesn't copy the contents of the string. It's just taking a slice just like whenever you pass a string to any other function. So, it refers to the same memory as what's passed in. So, if what it's passed is a string that refers to memory on the stack, then when it goes to print the message, it's going to be garbage, because the stack was unwound, and the static array is gone. Honestly, I'd argue that it's a bug that it allows you provide a message as a char[] instead of immutable(char)[]. It seems that the compiler is implicitly converting char[] to immutable(char)[] in this case, which is very bad. It would matter less if you were giving the assertion a char[] that referred to memory on the heap, but it still shouldn't be allowed. You really should be giving assertions a value that is an actual string that lives on the heap when providing a message, not a char[], and definitely not a char[] that's a slice of a static array. - Jonathan M Davis
Nov 21
parent reply David Zhang <straivers98 gmail.com> writes:
On Tuesday, 21 November 2017 at 19:05:21 UTC, Jonathan M Davis 
wrote:
 Well, the assertion is going to throw an AssertError, which 
 takes a string for its message. It doesn't copy the contents of 
 the string. It's just taking a slice just like whenever you 
 pass a string to any other function. So, it refers to the same 
 memory as what's passed in. So, if what it's passed is a string 
 that refers to memory on the stack, then when it goes to print 
 the message, it's going to be garbage, because the stack was 
 unwound, and the static array is gone.

 Honestly, I'd argue that it's a bug that it allows you provide 
 a message as a char[] instead of immutable(char)[]. It seems 
 that the compiler is implicitly converting char[] to 
 immutable(char)[] in this case, which is very bad. It would 
 matter less if you were giving the assertion a char[] that 
 referred to memory on the heap, but it still shouldn't be 
 allowed.

 You really should be giving assertions a value that is an 
 actual string that lives on the heap when providing a message, 
 not a char[], and definitely not a char[] that's a slice of a 
 static array.

 - Jonathan M Davis
Right. So a literal would work, because it's in the .data segment, or inlined. Basically, only strings from the heap, or that are compiled into the code are valid for assert messages. What kind of behavior would an assert from stdc have (id, betterC)?
Nov 21
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, November 21, 2017 19:13:36 David Zhang via Digitalmars-d-learn 
wrote:
 On Tuesday, 21 November 2017 at 19:05:21 UTC, Jonathan M Davis

 wrote:
 Well, the assertion is going to throw an AssertError, which
 takes a string for its message. It doesn't copy the contents of
 the string. It's just taking a slice just like whenever you
 pass a string to any other function. So, it refers to the same
 memory as what's passed in. So, if what it's passed is a string
 that refers to memory on the stack, then when it goes to print
 the message, it's going to be garbage, because the stack was
 unwound, and the static array is gone.

 Honestly, I'd argue that it's a bug that it allows you provide
 a message as a char[] instead of immutable(char)[]. It seems
 that the compiler is implicitly converting char[] to
 immutable(char)[] in this case, which is very bad. It would
 matter less if you were giving the assertion a char[] that
 referred to memory on the heap, but it still shouldn't be
 allowed.

 You really should be giving assertions a value that is an
 actual string that lives on the heap when providing a message,
 not a char[], and definitely not a char[] that's a slice of a
 static array.

 - Jonathan M Davis
Right. So a literal would work, because it's in the .data segment, or inlined. Basically, only strings from the heap, or that are compiled into the code are valid for assert messages.
Or any kind of exception message.
 What kind of behavior would an assert from stdc have (id,
 betterC)?
I don't know. It calls the C implementation at that point, so it won't throw an AssertError, but I don't know exactly how it's implemented. It probably just prints the message and then kills the program, so it'll probably work even with a slice of a static array, but I don't know for sure. However, unless the code is specifically versioned to only be betterC, it would be a really bad idea to rely on that behavior. And if assert ever gets fixed such that it properly rejects messages that aren't actually strings, passing it a char[] wouldn't work anymore anyway. - Jonathan M Davis
Nov 21
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, November 21, 2017 12:05:21 Jonathan M Davis via Digitalmars-d-
learn wrote:
 On Tuesday, November 21, 2017 18:49:40 David Zhang via Digitalmars-d-learn
 wrote:
 Hi,

 I've been trying to copy a string, then output it from an
 `assert(false)` statement, but the copy seems to be corrupted
 somehow.

 void main()
 {

   string str = "some string";

          //initializing `chars` with any value doesn't do anything

   char[64] chars;
   //char[64] chars = '!';

   //memcpy doesn't work either, though the output's different
   //import core.stdc.string;
   //memcpy(&chars[0], &str[0], str.length);

   chars[0..str.length] = str;
   assert(false, chars[0..str.length]);

 }

 What am I missing here?
Well, the assertion is going to throw an AssertError, which takes a string for its message. It doesn't copy the contents of the string. It's just taking a slice just like whenever you pass a string to any other function. So, it refers to the same memory as what's passed in. So, if what it's passed is a string that refers to memory on the stack, then when it goes to print the message, it's going to be garbage, because the stack was unwound, and the static array is gone. Honestly, I'd argue that it's a bug that it allows you provide a message as a char[] instead of immutable(char)[]. It seems that the compiler is implicitly converting char[] to immutable(char)[] in this case, which is very bad. It would matter less if you were giving the assertion a char[] that referred to memory on the heap, but it still shouldn't be allowed. You really should be giving assertions a value that is an actual string that lives on the heap when providing a message, not a char[], and definitely not a char[] that's a slice of a static array.
https://issues.dlang.org/show_bug.cgi?id=18002 - Jonathan M Davis
Nov 21
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Nov 21, 2017 at 12:05:21PM -0700, Jonathan M Davis via
Digitalmars-d-learn wrote:
 On Tuesday, November 21, 2017 18:49:40 David Zhang via Digitalmars-d-learn 
 wrote:
[...]
   char[64] chars;
   chars[0..str.length] = str;
   assert(false, chars[0..str.length]);
[...]
 Well, the assertion is going to throw an AssertError, which takes a
 string for its message. It doesn't copy the contents of the string.
 It's just taking a slice just like whenever you pass a string to any
 other function.  So, it refers to the same memory as what's passed in.
 So, if what it's passed is a string that refers to memory on the
 stack, then when it goes to print the message, it's going to be
 garbage, because the stack was unwound, and the static array is gone.
 
 Honestly, I'd argue that it's a bug that it allows you provide a
 message as a char[] instead of immutable(char)[]. It seems that the
 compiler is implicitly converting char[] to immutable(char)[] in this
 case, which is very bad. It would matter less if you were giving the
 assertion a char[] that referred to memory on the heap, but it still
 shouldn't be allowed.
Yeah, this is a bug. I filed an issue: https://issues.dlang.org/show_bug.cgi?id=18003 T -- The peace of mind---from knowing that viruses which exploit Microsoft system vulnerabilities cannot touch Linux---is priceless. -- Frustrated system administrator.
Nov 21