On 14 November 2014 11:20, Walter Bright via Digitalmars-d <[email protected]> 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.
I'm not quite clear; are you suggesting 'scope ref' would be a storage class? (looks like it, but it also affects mangling?) Please, consider Marc Schütz's existing proposal. Please, please, don't make scope a storage class. I have a lot of other issues with this proposal, but maybe I misunderstand it in principle...?
