Scope and Ref and Borrowing

Freddy via Digitalmars-d digitalmars-d at puremagic.com
Mon Nov 17 12:07:53 PST 2014


On Friday, 14 November 2014 at 01:21:07 UTC, Walter Bright wrote:
> Thought I'd bring this up as deadalnix is working on a related 
> proposal. It uses 'scope' in conjunction with 'ref' to resolve 
> some long standing @safe issues.
> ---------------------------------------
>
> **Background
>
> The goal of @safe code is that it is guaranteed to be memory 
> safe. This is mostly
> achieved, but there's a gaping hole - returning pointers to 
> stack objects when those
> objects are out of scope. This is memory corruption.
>
> The simple cases of this are disallowed:
>
>   T* func(T t) {
>     T u;
>     return &t; // Error: escaping reference to local t
>     return &u; // Error: escaping reference to local u
>   }
>
> But are is easily circumvented:
>
>   T* func(T t) {
>     T* p = &t;
>     return p;  // no error detected
>   }
>
> @safe deals with this by preventing taking the address of a 
> local:
>
>   T* func(T t) @safe {
>     T* p = &t; // Error: cannot take address of parameter t in 
> @safe function func
>     return p;
>   }
>
> But this is awfully restrictive. So the 'ref' storage class was 
> introduced which
> defines a special purpose pointer. 'ref' can only appear in 
> certain contexts,
> in particular function parameters and returns, only applies to 
> declarations,
> cannot be stored, and cannot be incremented.
>
>   ref T func(T t) @safe {
>     return t; // Error: escaping reference to local variable t
>   }
>
> Ref can be passed down to functions:
>
>   void func(ref T t) @safe;
>   void bar(ref T t) @safe {
>      func(t); // ok
>   }
>
> But the following idiom is far too useful to be disallowed:
>
>   ref T func(ref T t) @safe {
>     return t; // ok
>   }
>
> And if it is misused it can result in stack corruption:
>
>   ref T foo() @safe {
>     T t;
>     return func(t); // no error detected, despite returning 
> pointer to t
>   }
>
> The purpose of this proposal is to detect these cases at 
> compile time and disallow them.
> Memory safety is achieved by allowing pointers to stack objects 
> be passed down the stack,
> but those pointers may not be saved into non-stack objects or 
> stack objects higher on the stack,
> and may not be passed up the
> stack past where they are allocated.
>
> The:
>
>     return func(t);
>
> case is detected by all of the following conditions being true:
>
> 1. foo() returns by reference
> 2. func() returns by reference
> 3. func() has one or more parameters that are by reference
> 4. 1 or more of the arguments to those parameters are stack 
> objects local to foo()
> 5. Those arguments can be @safe-ly converted from the parameter 
> to the return type.
>    For example, if the return type is larger than the parameter 
> type, the return type
>    cannot be a reference to the argument. If the return type is 
> a pointer, and the
>    parameter type is a size_t, it cannot be a reference to the 
> argument. The larger
>    a list of these cases can be made, the more code will pass 
> @safe checks without requiring
>    further annotation.
>
> **Scope Ref
>
> The above solution is correct, but a bit restrictive. After 
> all, func(t, u) could be returning
> a reference to non-local u, not local t, and so should work. To 
> fix this, introduce the concept
> of 'scope ref':
>
>     ref T func(scope ref T t, T u) @safe {
>       return t; // Error: escaping scope ref t
>       return u; // ok
>     }
>
> Scope means that the ref is guaranteed not to escape.
>
>   T u;
>   ref T foo() @safe {
>     T t;
>     return func(t, u); // ok, u is not local
>     return func(u, t); // Error: escaping scope ref t
>   }
>
> This scheme minimizes the number of 'scope' annotations 
> required.
>
> **Out Parameters
>
> 'out' parameters are treated like 'ref' parameters for the 
> purposes of this document.
>
> **Inference
>
> Many functions can infer pure, @safe, and @nogc. Those same 
> functions can infer
> which ref parameters are 'scope', without needing user 
> annotation.
>
> **Mangling
>
> Scope will require additional name mangling, as it affects the 
> interface of the function.
>
> **Nested Functions
>
> Nested functions have more objects available than just their 
> arguments:
>
>   ref T foo() @safe {
>     T t;
>     ref T func() { return t; }
>     return func();  // should be disallowed
>   }
>
> On the plus side the body of the nested function is available 
> to the compiler for
> examination.
>
> **Delegates and Closures
>
> This one is a little harder; but the compiler can detect that t 
> is taken by reference,
> and then can assume that dg() is returning t by reference and 
> disallow it.
>
>   ref T foo() @safe {
>     T t;
>     ref T func() { return t; }
>     auto dg = &func;
>     return dg();  // should be disallowed
>   }
>
>
> **Overloading
>
> Scope does not affect overloading, i.e.:
>
>    T func(scope ref a);
>    T func(ref T b);
>
> are considered the same as far as overloading goes.
>
> **Inheritance
>
> Overriding functions inherit any 'scope' annotations from their 
> antecedents.
>
> **Limitations
>
> Arrays of references are not allowed.
> Struct and class fields that are references are not allowed.
> Non-parameter variables cannot be references.

Why not make the compiler copy the variable how's address is
taken to the heap like how a closure does it? Would it be hard
that optimize away?
----
int* c;
int main(){
      int a;
      int* b=&a;//a is now a reference to the heap
      c=&a;//safe
}
----


More information about the Digitalmars-d mailing list