www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 9283] New: "out ref" function attribute proposal to solve ref-accepting ref-returning memory safety issue

http://d.puremagic.com/issues/show_bug.cgi?id=9283

           Summary: "out ref" function attribute proposal to solve
                    ref-accepting ref-returning memory safety issue
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nobody puremagic.com
        ReportedBy: reachzach gmail.com


--- Comment #0 from Zach the Mystic <reachzach gmail.com> 2013-01-08 20:29:34
PST ---
This proposal plugs a hole in D's memory safety system. The problem is that the
compiler can't easily tell whether the return value of a function which takes a
local by reference should be treated as a local or global. It only occurs with
reference returns which also take a reference parameter:

ref int foh(ref int a) { return a; }
ref int go()
{
  int local;
  return foh(local); // Compiler needs help: treat as local or global?
}

This issue was only raised with the purpose of making  safe code truly safe,
but in general this error (if it is an error) should be caught, and there is no
computationally cheap way to do it without marking the function signature.

I propose that the compiler consider the above code an error by default. If the
reference parameter taking local is marked "scope" (assuming I understand the
"scope" attribute correctly), the compiler can ignore this issue and move on.

Which leaves the case where you need both a returnable reference return and a
non-scope reference parameter. For the compiler to run through the function
call searching for whether it returns a local or a global is too expensive, and
impossible when the PIMPL idiom is being used. Therefore, the choice is either
to disallow it, to allow it unsafely, or to make an addition to the function
attribute system.

The best part about this proposal is that there is a nice-looking way to add
the function attribute, using the keyword "out" (before parentheses) to mark a
function which promises not to return any of its reference parameters:

ref int lugs(ref int a) { return a; }
ref int h(ref int a)
{
  return lugs(a); // Okay

  int local;
  return lugs(local); // New error: the result of a function which accepts a
local as a non-scope reference and returns a reference is treated as local and
cannot be escaped unless that function is marked "out"

  int* p = &lugs(local); // Same error
}

int d;
out ref int saml(ref int a)
{
  return *(new int); // Fine
  return d; // Fine

  return a; // Error: a function marked "out" may not escape one of its
reference parameters
}

ref int lugh(ref int a) { return a; }
out ref int druh(ref int a)
{
  return lugh(a); // Error: a function marked "out" may not escape the result
of a function which accepts its reference parameter as a non-scope reference
and returns a reference unless that function is also marked "out"
}

out int boops(ref int a) {} // Error: a function marked "out" must return a
reference
out ref int bop(int a) {} // Error: a non-member function marked "out" must
accept at least one ref parameter 

If this system requires flexibility for hardcore systems programming, the
expression "cast (out)" has a nice ring to it.

// "cast(out)" transforms a local into a global:
out ref int lit(ref int a)
{
  return cast(out) a; // Obviously not  safe
}

// It can be used anywhere:
ref int hugs(ref int a) { return a; }
ref int g(ref int a)
{
  int local;
  return cast(out) (hugs(local)); // Okay
  return cast(out) local; // Okay??
  return hugs(cast(out) local); // Won't know what hit 'em
}

Because struct and class member functions which return ref may return their
fields, it applies to them even if they take no parameters:

struct S
{
  int _i;
  static int _s; 
  out ref int club() { return _i; } // Error: a member function marked "out"
may not escape a non-static field
  out ref int trob() { return _s; } // Okay
  out ref int blub() { return cast(out) _i; } //Okay
}

struct B { int _i; ref int plub() { return _i; } }
ref int bub()
{
  B b;
  return b.plub(); // Error: the result of a local instance's non-static method
which returns a reference is considered local and may not be escaped unless
that method is marked "out"

  int* i = &b.plub(); // Same error
}

The system does give more uses to the word "out", but does not however conflict
with existing uses (assuming the attribute "out" may only come before
parentheses in function signatures and not after).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 08 2013