Thread overview
Segfault when writing to module-scope dynamic array
Nov 18, 2019
rombankzero
Nov 18, 2019
Adam D. Ruppe
Nov 18, 2019
rombankzero
Nov 18, 2019
Adam D. Ruppe
Nov 18, 2019
rombankzero
November 18, 2019
Hi! Can anybody explain why this snippet segfaults when I try to run it:

> char[] array = new char[5];
>
> void main()
> {
>     array[0] = 'a';
> }

It works fine if I move the array into main, or (strangely) if I change its type to ubyte[] instead of char[], or if I merely read the array without modifying it. I assumed it would heap-allocate a mutable dynamic array before main runs, but maybe I've misunderstood what `new` expressions do at module scope? Or is this some special case for char arrays (strings)?

I really don't know what to make of it.
November 18, 2019
On Monday, 18 November 2019 at 12:31:10 UTC, rombankzero wrote:
>> char[] array = new char[5];

It is almost always wrong to new things at module, class, or struct scope, since this create it in the static data segment.

My guess is the compiler is seeing a static string and incorrectly putting it in the read-only segment with the other strings, and then writing to it thus triggers that segfault.

just a guess, and if correct, this is a dmd bug.

But I'd still recommend doing the `new` inside a function/module constructor anyway. That takes the static aspect out entirely and prevents some surprising bugs (right now the new is run at CTFE, not a huge deal here but with like classes it surprises a lot of people as the default pointer is shared between instances!)

> I assumed it would heap-allocate a mutable dynamic array before main runs

but yeah this is NOT what it does. It runs new at *compile time* which means that `array` variable is pointing to static memory, it is not heap allocated at all.
November 18, 2019
On Monday, 18 November 2019 at 12:42:26 UTC, Adam D. Ruppe wrote:
> My guess is the compiler is seeing a static string and incorrectly putting it in the read-only segment with the other strings, and then writing to it thus triggers that segfault.

Yeah, that appears to be what's happening. For the record, I tried it on both Linux and Windows, and only got a segfault on Linux. And the fact that the ubyte[] case works only added to the confusion.


On Monday, 18 November 2019 at 12:42:26 UTC, Adam D. Ruppe wrote:
> but yeah this is NOT what it does. It runs new at *compile time* which means that `array` variable is pointing to static memory, it is not heap allocated at all.

Are there cases where this behavior is useful? Because it seems pretty surprising to me. Like, when I see the "new" keyword, I immediately think "heap allocation", not anything being put into the data segment.
November 18, 2019
On Monday, 18 November 2019 at 13:22:20 UTC, rombankzero wrote:
> Yeah, that appears to be what's happening. For the record, I tried it on both Linux and Windows, and only got a segfault on Linux.

Yeah, that's consistent with other strings too. (The D type system calls both immutable, but the OS is looser on Windows. at least 32 bit.)

> Are there cases where this behavior is useful?

Yeah, it is cool for sentinel objects and default settings. Usually best when coupled with immutable to prevent more confusion but mutable versions sometimes make sense too.

But it is rare and if you aren't specifically looking for it knowing the details, it isn't right. And since the syntax looks so normal it trips up a LOT of people.

I think the compiler should probably start to error on it, and if you know what you're doing, maybe mark it special somehow. idk.


November 18, 2019
On Monday, 18 November 2019 at 13:28:26 UTC, Adam D. Ruppe wrote:
> But it is rare and if you aren't specifically looking for it knowing the details, it isn't right. And since the syntax looks so normal it trips up a LOT of people.
>
> I think the compiler should probably start to error on it, and if you know what you're doing, maybe mark it special somehow. idk.

Well, my fingers are singed. I'll know not to do that again, for what it's worth :)