ref is unsafe

Zach the Mystic reachBUTMINUSTHISzach at gOOGLYmail.com
Thu Jan 3 12:49:17 PST 2013


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.



More information about the Digitalmars-d mailing list