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.

Reply via email to