On Sunday, 30 December 2012 at 08:38:27 UTC, Jonathan M Davis
wrote:
And maybe another solution which I can't think of at the moment
would be
better. But my point is that we currently have a _major_ hole
in SafeD thanks
to the combination of ref parameters and ref return types, and
we need to find
a solution.
- Jonathan M Davis
Related: http://d.puremagic.com/issues/show_bug.cgi?id=8838
I've thought about how I think the attributes should work if D is
forced to use them. This was the first system I came up with, but
as you'll see below, the system can be simplified by ignoring
@safe-ty altogether:
Two attributes: @saferef and @inoutref
// "@saferef" is semantically equivalent to "@safe @inoutref"
@saferef ref int fupz(ref int a)
{
somethingUnsafe(); // Error
return a; //Okay
}
// The same function won't work with just @safe
@safe ref int fuz(ref int a)
{
return a; // Error: a @safe function which returns a reference
to
// a variable deriving from one of its parameters
must be
// marked @saferef
}
// Basic rule against using it when not necessary:
// a @saferef or @inoutref function must both accept and return a
ref
@saferef int validate1(ref int a) { return a; } // Error
@inoutref ref int validate2(int a) { return a; } // Error
// @saferef's are chained by compiler enforcement:
@saferef ref int fonz(ref int a) { return a; }
@safe ref int frooz(ref int a)
{
return fonz(a); // Error: a function which returns the result
of one of
// its parameters being passed to a @saferef or
@inoutref
// function must itself be marked @saferef or
@inoutref
}
// The problem of escaping local variables:
@saferef ref int fonz(ref int a) { return a; }
ref int dollop()
{
int local;
return fonz(local); // Error: a function may not return the
result of a local variable passed to a @saferef or an @inoutref
function
}
// @inoutref may be used when you have otherwise un-safe code:
@inoutref ref int froes(ref int a)
{
/+…some unsafe code…+/
return a;
}
ref int f()
{
int local;
return froes(local); // Bug caught now even in @system code
}
// An enhancement: mark harmless parameters as @saferef
@saferef ref int twoParams(@saferef ref int a, ref int b)
{
return a; // Error: a @saferef or @inoutref function may not
return a reference derived from a parameter marked @saferef
return b; // Fine
}
// Only @saferef or @inoutref functions would be able to use
@saferef parameters:
ref int zorf(@saferef ref int a, ref int b) {} // Error
So I typed all of that out and realized that a simpler
alternative would be to ignore @safe altogether and have the
@inoutref functionality be on by default. The only attribute now
required would be @outref, which could be simplified to just
"out" so long as it appeared *before* the parameter list, since
it could be confused for an out contract if it came afterwards.
So:
"@saferef" <=> "@safe @outref" is unnecessary because all
functions are checked, not just @safe ones.
ref int lugs(ref int a)
{
return a; // Okay
}
ref int h(ref int a)
{
return lugs(a); // Okay
int local;
return lugs(local); // Error: may not return the result of a
local variable
// passed to a function which both accepts
and returns a
// ref unless that function is marked "@outref"
}
int d;
@outref ref int saml(ref int a)
{
return *(new int); // Fine
return d; // Fine
return a; // Error: a function marked "@outref" may not return
a reference
// deriving from one of its parameters
}
ref int lugs(ref int a) { return a; }
@outref ref int druh(ref int a)
{
return lugs(a); // Error: a function marked @outref may not
return the result
// of one of its parameters being passed to a
function unless
// that function is itself marked @outref
}
// Must both accept and return a reference
@outref int boops(ref int a) {} // Error
@outref ref int bop(int a) {} // Error
// Harmless parameters may be marked @trusted:
@outref ref int lit(@trusted ref int a, ref int b)
{
return a; // Passes based on the honor system
return b; // Error
}
The second system is much simpler, and it's only a little more
computationally expensive than the first, since the signature of
all functions called with local variables must be scanned for ref
output and input, not just safe ones.