Thread overview
Unexpected memory reuse
Jul 31, 2014
Anonymous
Jul 31, 2014
Anonymous
Jul 31, 2014
Sean Kelly
Jul 31, 2014
Anonymous
Jul 31, 2014
Marc Schütz
Jul 31, 2014
bearophile
Jul 31, 2014
Sean Kelly
July 31, 2014
module test;
import std.stdio;

class buffer(T, size_t sz) {
	auto arr = new T[sz];
	enum end = sz-1;
}

void foo(T, size_t sz)() {
	auto buf = new buffer!(T,sz);
	writeln("before ", buf.arr);
	foreach(ref ele; buf.arr) ++ele;
	writeln("after ", buf.arr);
}

unittest {
	foo!(uint,4);
	foo!(uint,4);

	auto a = new buffer!(uint,4);
	writeln(a.arr);
	auto b = new buffer!(uint,4);
	writeln(b.arr);
}

void main() {
	auto c = new buffer!(uint,4);
	writeln(c.arr);
}

rdmd -unittest test.d
before [0, 0, 0, 0]
after [1, 1, 1, 1]
before [1, 1, 1, 1]
after [2, 2, 2, 2]
a [2, 2, 2, 2]
b [2, 2, 2, 2]
c [2, 2, 2, 2]
[Finished in 1.3s]

I narrowed this down after much frustration. I'm using some fixed size buffers and thought it useful to define the size as part of the type, from which other aspects could be statically derived. Apparently it's not so useful. So I guess I can refactor the buffer's array length into the constructor or something, but I really didn't see this kind of memoization(?) coming.
July 31, 2014
Whoops, that is writeln("a ",a.arr); and so on.
July 31, 2014
This looks like an optimizer bug.  Do you see the same result
with -release set vs. not, etc?
July 31, 2014
On Thursday, 31 July 2014 at 18:51:09 UTC, Sean Kelly wrote:
> This looks like an optimizer bug.  Do you see the same result
> with -release set vs. not, etc?

I get it regardless of -release or -O.

Replacing the arr declaration with T[sz] arr; fixes the problem.
July 31, 2014
On Thursday, 31 July 2014 at 18:30:41 UTC, Anonymous wrote:
> module test;
> import std.stdio;
>
> class buffer(T, size_t sz) {
> 	auto arr = new T[sz];

This allocates an array with `sz` elements once _at compile time_, places it somewhere into the executable, and uses its address as the default initializer for the member `arr`. All instances of `buffer` (with the same template parameters) that you create and don't change `arr` have it point to the same memory.

Use this instead:

    class buffer(T, size_t sz) {
        T[sz] arr;
        enum end = sz-1;
    }

This embeds `arr` into the class, instead of making it a reference to a dynamic array. If you want the latter, use this:

    class buffer(T, size_t sz) {
        T[] arr;
        enum end = sz-1;
        this() {
            arr = new T[sz];
        }
    }
July 31, 2014
Marc Schütz:

>> class buffer(T, size_t sz) {
>> 	auto arr = new T[sz];
>
> This allocates an array with `sz` elements once _at compile time_, places it somewhere into the executable, and uses its address as the default initializer for the member `arr`.

Right. It's not a compiler bug. Dmd/ldc are working correctly.

Bye,
bearophile
July 31, 2014
On Thursday, 31 July 2014 at 19:28:24 UTC, Marc Schütz wrote:
> On Thursday, 31 July 2014 at 18:30:41 UTC, Anonymous wrote:
>> module test;
>> import std.stdio;
>>
>> class buffer(T, size_t sz) {
>> 	auto arr = new T[sz];
>
> This allocates an array with `sz` elements once _at compile time_, places it somewhere into the executable, and uses its address as the default initializer for the member `arr`. All instances of `buffer` (with the same template parameters) that you create and don't change `arr` have it point to the same memory.

Huh.  For some reason I thought in-class initializations like
this were effectively rewritten to occur as a part of the class
ctor.  But looking at the docs I guess this actually affects the
format of the static initializer.