On Thursday, 31 October 2013 at 17:34:21 UTC, Daniel Davidson
wrote:
On Thursday, 31 October 2013 at 16:16:36 UTC, bearophile wrote:
That's wrong code, you are escaping a reference to memory (of
rc variable) allocated in the stack frame of foo(). The D
compiler is not smart enough to recognize the bug. There are
optimizations that patch and avoid this bug (like inlining, or
allocating rc inside the stack frame of the main) but you
can't rely on them.
Ahh ok. Because of issues with const members (copying them when
they are composed in other classes does not work well) I'm
trying to get around the copy by storing reference data as
const(T) *. This is why I'm asking. So here are some follow ups:
- As I see it there is grave risk. Is the risk introduced by
the fact that I am storing a member as const(T)*?
Risk is that pointer to local variable is escaped, not because it
is stored in const qualified pbject.
- I assume that if I had created the RC instance on the heap
there would be no problems. Is there a way in general to ensure
that a const(T)* is referring to something on the heap as
opposed to the stack (ideally at compile time).
Yes, you can explicitly allocate on heap. You can check whether
data is on heap, stack, tls, or just global object by inspecting
pointer at runtime. Ideally there would be such function in
druntime. It is impossible to do this in CT (except if comiler
support flow analysis and can prove in some scenarious that data
is on stack or not, but due to separate compilation it is
impossible to do in general case) (and probably shouldn't).
- What is the root problem - having a const(T)* member which
then requires code to take address, or taking address? My first
thought was, just never take address of local static variable -
which is generally good advice. But once you are in a function
that takes ref const(T) you can take the address of that as
well - see modification below. This suffers the same problem
but in a less obvious way.
Any suggestions welcome.
Thanks
Dan
import opmix.mix;
import plus.tvm.rate_curve;
import std.stdio;
struct RC {
this(this) { data = data.dup; }
int[] data;
}
struct T {
const(RC) *rc;
void goo() {
writeln("Data is ", rc.data);
}
}
T goo(ref RC rc) {
return T(&rc);
}
T foo() {
RC rc = { [1,2,3] };
writeln("Address is ", &rc);
return goo(rc);
}
void main() {
T t = foo();
t.goo();
writeln("Address is ", t.rc);
}
Yes, this is known issue. There are lots of other tricks to break
the language.