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