Bug 19111 – Returning a reference to local array results in garbage
Status
RESOLVED
Resolution
INVALID
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-07-23T01:30:14Z
Last change time
2018-07-26T10:34:02Z
Keywords
safe
Assigned to
No Owner
Creator
Alex
Comments
Comment #0 by alex — 2018-07-23T01:30:14Z
See code below.
This is a reduction of a real life bug.
Arrays declared like int[10] a, appear to behave differently to auto a = new int[10].
I can understand this might be intended for efficiency etc, but it is source of undefined behaviour that I thought D design intent was to avoid.
In the below, the code can work as long as makeRef happens to modify the array in a way that makes it need to reallocate the array on the heap.
If that doesn't happen, a reference is returned to the local variable.
With my previous understanding of how arrays were garbage collected, nice to slice etc it is really not clear that it is possible to return a reference to a locally declared variable that can go out of scope.
I suggest that int[10] should be on the heap unless the compiler can be sure that there isn't a way for a reference to escape.
That way people writing high performance code that actually need a stack array have to write code in a way that convinces the compiler not to promote it to the stack, and everyone else gets the code that avoids undefined behavior.
import std.stdio;
int[] makeRef(int[] a)
{
a ~= 43; //if this is removed the result of getArray contains garbage.
return a;
}
int[] getArray()
{
int[10] a;
a []= 42;
//return a[0..3]; //Error: returning cast(int[])a escapes a reference to local variable a
return makeRef(a); //compiles without error, and returns garbage, unless makeRef happens to modify a
}
void main()
{
writefln("%s",getArray());
}
Comment #1 by slavo5150 — 2018-07-23T02:10:19Z
Using `@safe` with `-dip1000` seems to catch the error:
import std.stdio;
@safe:
int[] makeRef(int[] a)
{
return a;
}
int[] getArray()
{
int[10] a;
a []= 42;
return makeRef(a);
}
void main()
{
writefln("%s",getArray());
}
$ dmd -dip1000 test.d
https://run.dlang.io/is/hIoDsB
onlineapp.d(14): Error: reference to local variable a assigned to non-scope parameter a calling `onlineapp.makeRef`
Comment #2 by dfj1esp02 — 2018-07-26T09:50:50Z
Safe D and DIP1000 were created for this.
int[] makeRef(return int[] a)
{
return a;
}
int[] getArray()
{
int[10] a;
a []= 42;
return makeRef(a);
}
void main()
{
writefln("%s",getArray());
}
Error: returning makeRef(cast(int[])a) escapes a reference to local variable a
Comment #3 by dfj1esp02 — 2018-07-26T10:34:02Z
(In reply to Alex from comment #0)
> I suggest that int[10] should be on the heap unless the compiler can be sure
> that there isn't a way for a reference to escape.
LDC does the opposite: it performs D-specific optimizations and promotes heap allocations to stack if it detects that the allocation is properly scoped.