On Wednesday, 25 February 2015 at 18:08:55 UTC, deadalnix wrote:
On Wednesday, 25 February 2015 at 01:12:15 UTC, Zach the Mystic
wrote:
int r; // declaration scopedepth(0)
void fun(int a /*scopedepth(0)*/) {
int b; // depth(1)
{
int c; // depth(2)
{
int d; // (3)
}
{
int e; // (3)
}
}
int f; // (1)
}
You have element of differing lifetime at scope depth 0 so far.
Sorry for the delay.
I made a mistake. Parameter `a` will have a *declaration* scope
of 1, just like int b above. It's *reference* scope will have
depth 0, with the "mystery" bit for the first parameter set.
Principle 5: It's always un@safe to copy a declaration scope
from a higher scopedepth to a reference variable stored at
lower scopedepth. DIP69 tries to banish this type of thing
only in `scope` variables, but I'm not afraid to banish it in
all @safe code period:
void gun() @safe {
T* t; // t's declaration depth: 1
T u;
{
T* uu = &u; // fine, this is normal
T tt;
t = &tt; // t's reference depth: 2, error, un@safe
}
// now t is corrupted
}
Bingo. However, when you throw goto into the mix, weird thing
happens. The general idea is good but need refining.
I addressed this further down, in Principle 10. My proposed
solution has the compiler detecting the presence of code which
could both 1) be visited again (through a jump label or a loop)
and 2) is in a branching condition. In these cases it pushes any
statement which copies a reference onto a special stack. When the
branching condition finishes, it revisits the stack, "reheating"
the scopes in reverse order. If there is a way to defeat this
technique, it must be very convoluted, since the scopes do
nothing but accumulate possibilities. It may even be
mathematically impossible.
Principle 7: In this system, all scopes are *transitive*: any
reference type with double indirections inherits the scope of
the outermost reference. Think of it this way:
It is more complex than that, and this is where most proposals
fail short (including this one and DIP69). If you want to
disallow the assignment of a reference to something with a
short lifetime, you can't consider scope transitive when used
as a lvalue. You can, however, consider it transitive when used
as an rvalue.
The more general rule is that you want to consider the largest
possible lifetime of an lvalue, and the smallest possible one
for an rvalue.
When going through an indirection, that will differ, unless we
choose to tag all indirections, which is undesirable.
I'm unclear about what you're saying. Can you give an example in
code?
Principle 8: Any time a reference is copied, the reference
scope inherits the *maximum* of the two scope depths:
That makes control flow analysis easier, so I can buy this :)
Principle 8: We don't need to know! For all intents and
purposes, a reference parameter has infinite lifetime for the
duration of the function it is compiled in. Whenever we copy
any reference, we do a bitwise OR on *all* of the mystery
scopes. The new reference accumulates every scope it has ever
had access to, directly or indirectly.
That would allow to copy a parameter reference to a global,
which is dead unsafe.
Actually, it's not unsafe, so long as you have the parameter
attribute `noscope` (or possibly `static`) working for you:
void fun(T* a) {
static T* t;
*t = a; // this might be safe
}
The truth is, this *might* be safe. It's only unsafe if the
parameter `a` is located on the stack. From within the function,
the compiler can't possibly know this. But if it forces you to
mark `a` with `noscope` (or is allowed to infer the same), it
tells the caller all it needs to know about `a`. Simply put, it's
an error to pass a local to a `noscope` parameter. And it runs
all the way down: any parameter which it itself passed to a
`noscope` parameter must also be marked `noscope`. (Note: I'm
actually preferring the name `static` at this point, but using
`noscope` for consistency):
T* fun(noscope T* a) {
static T* t;
*t = a; // this might be safe
}
void tun(T* b) {
T c;
fun(&c); // error, local
fun(b); // error, unless b also marked (or inferred) `noscope`
}
There is some goodness in there. Please address my comment and
tell me if I'm wrong, but I think you didn't covered all bases.
The only base I'm really worried about is the lvalue vs rvalue
base. Hopefully we can fix that!